一、批量执行 SQL 语句
1、当需要成批插入或者更新记录时,可以采用 Java 的批量更新机制,这一机制允许多条语句一次性提交给数据库批量处理。
2、使用 PreparedStatement 可以实现批量数据的操作。
3、update、delete 本身就具有批量操作的效果。此时的批量操作,主要指的是批量插入,使用 PreparedStatement 实现高效的批量插入操作。
4、JDBC 的批量处理语句包括下面三个方法:
addBatch(String):添加需要批量处理的SQL语句或是参数;
executeBatch():执行批量处理语句;
clearBatch():清空缓存的数据
通常我们会遇到两种批量执行 SQL 语句的情况:
① 多条 SQL 语句的批量处理;
② 一个 SQL 语句的批量传参;
二、高效的批量插入
1、举例:向数据表中插入 20000 条数据
数据库中提供一个 goods 表,创建如下:
create table goods (
id int primary key auto_increment,
name varchar(20)
);
2、实现层次一:使用 Statement
代码实现:
1 Connection conn = JDBCUtils.getConnection();
2 Statement st = conn.createStatement();
3 for(int i = 1;i <= 20000;i++){
4 String sql = "insert into goods(name) values('name_' + "+ i +")";
5 st.executeUpdate(sql);
6 }
3、实现层次二:使用 PreparedStatement
代码实现:
1 @Test
2 public void testInsert2() {
3 Connection conn = null;
4 PreparedStatement ps = null;
5 try {
6
7 long start = System.currentTimeMillis();
8
9 conn = JDBCUtils.getConnection();
10 String sql = "insert into goods(name)values(?)";
11 ps = conn.prepareStatement(sql);
12 for(int i = 1;i <= 20000;i++){
13 ps.setObject(1, "name_" + i);
14
15 ps.execute();
16 }
17
18 long end = System.currentTimeMillis();
19
20 System.out.println("花费的时间为:" + (end - start));//20000:83065
21 } catch (Exception e) {
22 e.printStackTrace();
23 }finally{
24 JDBCUtils.closeResource(conn, ps);
25 }
26 }
使用 PreparedStatement 可以进行预编译语句的执行,在数据库服务端会把编译器编译后的语句缓存下来,那么下次调用时只有相同的预编译语句一样就不需再编译了,直接将参数传入语句中就会执行,效率得到了提升。
4、实现层次三:使用Batch
修改1:需要使用 addBatch() / executeBatch() / clearBatch() 的API;
修改2:MySQL 服务器默认是关闭批处理的,我们需要通过一个参数,让MySQL开启批处理的支持。
?rewriteBatchedStatements=true 写在配置文件的url后面
例如:url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true
修改3:使用更新的 MySQL 驱动,驱动:mysql-connector-java-5.1.37-bin.jar
代码实现:
1 @Test
2 public void testInsert3() {
3 Connection conn = null;
4 PreparedStatement ps = null;
5 try {
6
7 long start = System.currentTimeMillis();
8
9 conn = JDBCUtils.getConnection();
10 String sql = "insert into goods(name)values(?)";
11 ps = conn.prepareStatement(sql);
12 for(int i = 1;i <= 1000000;i++){
13 ps.setObject(1, "name_" + i);
14
15 //1."攒"sql
16 ps.addBatch();
17
18 if(i % 500 == 0){
19 //2.执行batch
20 ps.executeBatch();
21
22 //3.清空batch
23 ps.clearBatch();
24 }
25
26 }
27
28 long end = System.currentTimeMillis();
29
30 System.out.println("花费的时间为:" + (end - start));//20000:83065 -- 565
31 } catch (Exception e) { //1000000:16086
32 e.printStackTrace();
33 }finally{
34 JDBCUtils.closeResource(conn, ps);
35
36 }
37
38 }
通过Batch批处理的方式,可以先把SQL“收集”起来,等积攒到一定的条数,然后再让数据库执行,这样一来就可以有效的减少与数据库的交互操作(减少IO),再次对效率的提高。
5、实现层次四:设置连接不允许自动提交数据
上面的 Batch 操作,效率来说已经不错了,但是还可以进一步优化:
1 @Test
2 public void testInsert4() {
3 Connection conn = null;
4 PreparedStatement ps = null;
5 try {
6
7 long start = System.currentTimeMillis();
8
9 conn = JDBCUtils.getConnection();
10
11 //设置不允许自动提交数据
12 conn.setAutoCommit(false);
13
14 String sql = "insert into goods(name)values(?)";
15 ps = conn.prepareStatement(sql);
16 for(int i = 1;i <= 1000000;i++){
17 ps.setObject(1, "name_" + i);
18
19 //1."攒"sql
20 ps.addBatch();
21
22 if(i % 500 == 0){
23 //2.执行batch
24 ps.executeBatch();
25
26 //3.清空batch
27 ps.clearBatch();
28 }
29
30 }
31
32 //提交数据
33 conn.commit();
34
35 long end = System.currentTimeMillis();
36
37 System.out.println("花费的时间为:" + (end - start));//20000:83065 -- 565
38 } catch (Exception e) { //1000000:16086 -- 5114
39 e.printStackTrace();
40 }finally{
41 JDBCUtils.closeResource(conn, ps);
42 }
43 }
当把自动提交事务关闭后,会把所有的 SQL 都执行完毕之后再进行事务的提交,相当于上面的执行一部分就进行提交,效率也有所提高(可能并不是很明显)。