一、JDBC事务
说起事务相信学过数据库同学都有一些印象,在数据库中事务就是一段不可分割的代码,这段代码要么都执行要么都不执行。那么在JDBC中我们是如何创建一个事务的呢?
我们先来看一个例子,假如我有一个银行系统此时老王要给老李转账1000元,这时候我们通常是先判断老王的余额是否大于等于1000,接着在将老王的账户减1000同时将老李的账户加上1000。这是我们通常的一个做法如以下代码:
public static void main(String[] args) throws Exception {
// 1.建立连接
Connection conn = JDBCUtil.getConn();
// 2.创建sql
String sql = "select * from account where name = ? and money > ?";
// 3.赋值、执行sql
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, "lw");
ps.setInt(2, 1000);
ResultSet res = ps.executeQuery();
if (!res.next()) {
throw new RuntimeException("no money");
}
// 4.钱够,开始转账
sql = "update account set money = money - ? where name = ?";
// 3.赋值、执行sql
ps = conn.prepareStatement(sql);
ps.setInt(1, 1000);
ps.setString(2, "lw");
ps.executeUpdate();
sql = "update account set money = money + ? where name = ?";
// 3.赋值、执行sql
ps = conn.prepareStatement(sql);
ps.setInt(1, 1000);
ps.setString(2, "xl");
ps.executeUpdate();
JDBCUtil.close(conn, ps, res);
}
上述代码有什么问题呢?我们有没有想过万一老王再给老李转账的过程中系统出错了呢,比如说在执行完将老王的账户减1000时系统出错了,此时老李的账户并未增加1000元,而老王却少了一千。这不是很要命嘛。。。所以我们就可以将上述代码写成一段事务,事务里面的语句要么都执行要么都不执行。
其实将JDBC设置事务很简单,我们只需要在事务开始前的代码加上setAutoCommit(false)即可将事务设置成手动提交,然后在事务结束的地方设置commit()将事务手动提交上去就可以了。当然我们也可设置回滚语句rollback,具体代码如下所示:
public static void main(String[] args) throws Exception {
// 1.建立连接
Connection conn = JDBCUtil.getConn();
PreparedStatement ps = null;
ResultSet res = null;
try {
// 2.创建sql
String sql = "select * from account where name = ? and money > ?";
// 3.赋值、执行sql
ps = conn.prepareStatement(sql);
ps.setString(1, "lw");
ps.setInt(2, 1000);
res = ps.executeQuery();
if (!res.next()) {
throw new RuntimeException("no money");
}
conn.setAutoCommit(false);
// 4.钱够,开始转账
sql = "update account set money = money - ? where name = ?";
// 3.赋值、执行sql
ps = conn.prepareStatement(sql);
ps.setInt(1, 1000);
ps.setString(2, "lw");
ps.executeUpdate();
sql = "update account set money = money + ? where name = ?";
// 3.赋值、执行sql
ps = conn.prepareStatement(sql);
ps.setInt(1, 1000);
ps.setString(2, "xl");
ps.executeUpdate();
conn.commit();
} catch (Exception e) {
e.printStackTrace();
conn.rollback();
} finally {
JDBCUtil.close(conn, ps, res);
}
}
二、批处理
1.什么是批处理
批处理就是一次性执行多条Sql语句,允许多条语句一次性提交给数据库批量处理,批处理要比比单独提交处理的效率更高。既然说批处理效率要比单独处理高,那么我们就做一下比较
单独处理代码如下:
public static void main(String[] args) throws Exception {
Connection conn = JDBCUtil.getConn();
String sql = "insert into student(id, name, age)values(?,?,?)";
PreparedStatement ps = conn.prepareStatement(sql);
long begin = System.currentTimeMillis();
for (int i = 1; i <= 200; i++) {
ps.setInt(1, i);
ps.setString(2, "老王");
ps.setInt(3, 20+i);
ps.executeUpdate();
}
long end = System.currentTimeMillis();
System.out.println(end - begin);
JDBCUtil.close(conn, ps, null);
}
批处理代码如下:
public static void main(String[] args) throws Exception {
Connection conn = JDBCUtil.getConn();
String sql = "insert into student(id, name, age)values(?,?,?)";
PreparedStatement ps = conn.prepareStatement(sql);
long begin = System.currentTimeMillis();
for (int i = 1; i <= 200; i++) {
ps.setInt(1, i);
ps.setString(2, "老王");
ps.setInt(3, 20+i);
//加入批处理
ps.addBatch();
}
//执行批处理
ps.executeBatch();
long end = System.currentTimeMillis();
System.out.println(end - begin);
JDBCUtil.close(conn, ps, null);
}
2.怎么使用批处理
在JDBC中使用批处理很简单就只有两个函数:
- addBatch()将sql加入批处理
- executeBatch()执行批处理
默认情况下Mysql不支持批处理,但是在5.1.13开始我们只要在连接数据库中添加一个rewriteBatchedStatement=true参数即可。具体代码见上文
三、存储图片
我们在开发当中存储图片一般都是将文件的储存路径保存在数据库当中,以后再通过从数据库当中取出的路径到该路劲下读取信息。其实数据库本身也是可以存储文件的,数据库是以二进制流的形式进行存储文件的,我们在开发当中一般不使用这种方法,所以我们只需要了解即可。
数据库当中的BLOB给我们提供了存储图片、音频、视频等多媒体信息的功能大致分为以下几种类型,主要通过能存储数据的大小来区别
TINYBLOB 255个字节
BLOB 65535字节
MEDIUMBLOB 16M
LONGBLOB 4G
以下代码演示存入图片:
public static void main(String[] args) throws Exception {
Connection conn = JDBCUtil.getConn();
String sql = "insert into student(id, img)values(?,?)";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setInt(1, 1);
FileInputStream in = new FileInputStream("C:\Users\瘦明月\Desktop\jdbc4.png");
ps.setBlob(2, in);
ps.executeUpdate();
JDBCUtil.close(conn, ps, null);
}
以下代码演示取出图片
public static void main(String[] args) throws Exception {
Connection conn = JDBCUtil.getConn();
String sql = "select * from student where id = ?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setInt(1, 1);
ResultSet res = ps.executeQuery();
if(res.next()) {
//获取图片
Blob blob = res.getBlob("img");
//获取图片二进制流
InputStream bs = blob.getBinaryStream();
//把程序文件写入磁盘
Files.copy(bs, Paths.get("C:\Users\瘦明月\Desktop\jdbc.png"));
}
JDBCUtil.close(conn, ps, null);
}
这样我们就能从指定的地址访问到图片啦。
四、获取自动生成的主键
有时候我们在设计表时一般会设置主键自动增长,这样我们在插入数据时可以不用指定主键是多少。但是有时候我们又需要知道系统自动生成的主键是多少,好让我们能获取到刚才插入的数据。正如下图所示一般,我们先引导用户输入用户名密码进行注册,紧接着注册成功后我们需要获取用户的id并引导他完善信息。
-
使用Statement获取key
创建语句时,设置可以获取主键
st.executeUpdate(sql,Statement.RETURN_GENERATED_KEYS)
通过语句对象的getGeneratedKeys获取主键public static void main(String[] args) throws Exception { Connection conn = JDBCUtil.getConn(); String sql = "insert into student(name, age)values('laowang', 15)"; Statement st = conn.createStatement(); st.executeUpdate(sql, Statement.RETURN_GENERATED_KEYS); ResultSet keys = st.getGeneratedKeys(); if(keys.next()) { int key = keys.getInt(1); System.out.println(key); } JDBCUtil.close(conn, st, null); }
-
使用preparedStatement
在创建语句时,传入参数
Statement.RETURN_GENERATED_KEYS
通过语句对象的getGeneratedKeys获取主键public static void main(String[] args) throws Exception { Connection conn = JDBCUtil.getConn(); String sql = "insert into student(name, age)values(?, ?)"; PreparedStatement ps = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); ps.setString(1, "laoliu"); ps.setInt(2, 20); ps.executeUpdate(); ResultSet res = ps.getGeneratedKeys(); if(res.next()) { int id = res.getInt(1); System.out.println(id); } JDBCUtil.close(conn, ps, res); }