你在工作中会在for循环里面写SQL吗,如果有这种代码,一定要优化 这种优化可以使系统的性能得到质的提升、
首先我们看一个JDBC最基础的批处理方式:
package javaee.net.cn.jdbc; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.SQLException; public class TestTransaction { public static void main(String[] args) { System.err.println("插入一千条数据开始时间"+ System.currentTimeMillis()); Connection conn = null; PreparedStatement stmt = null; try { //1.拿到数据库的驱动 Class.forName("com.mysql.jdbc.Driver"); //拿到数据库的连接 conn = DriverManager .getConnection("jdbc:mysql://119.23.50.194:3306/test?user=root&password=????"); //不让事物自动提交 conn.setAutoCommit(false); String sql = "insert into test_num (num,age) values (?,?)"; //创建一个statement stmt = conn.prepareStatement(sql); for(int i=3000;i<4000;i++){ stmt.setInt(1,i); stmt.setInt(2,26); stmt.addBatch(); } stmt.executeBatch(); //提交事物并释放此 Connection 对象当前持有的所有数据库锁。 conn.commit(); //详见下面解释 conn.setAutoCommit(true); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch(SQLException e) { e.printStackTrace(); try { if(conn != null) { conn.rollback(); conn.setAutoCommit(true); } } catch (SQLException e1) { e1.printStackTrace(); } }finally { try { if(stmt != null) stmt.close(); if(conn != null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } } System.err.println("插入一千条数据结束时间"+ System.currentTimeMillis()); } } /** 插入一千条数据开始时间1528126739785 插入一千条数据结束时间1528126776692 打印出来的结果 批处理需要37s */
jdbc的批处理优点是,获取一个PreparedStatement对象,可以执行多条insert语句(update同理) 这比for循环里面写SQL大大提高了效率
如果改成如下的for循环
String sql = "insert into test_num (num,age) values (?,?)";
for(int i=5000;i<6000;i++){
//创建一个statement
stmt = conn.prepareStatement(sql);
stmt.setInt(1,i);
stmt.setInt(2,26);
stmt.execute();
}
插入一千条数据开始时间1528127559241
插入一千条数据结束时间1528127596059
什么?批处理37s, for循环创建一千个 stmt对象也需要37s 明显的是没有看到批处理的差距啊?
今天写博客做了实验之后 才发现对MySQL的批处理 错误理解了三年。。看了篇大牛的文章 https://blog.csdn.net/woshiwxw765/article/details/7619638
并不是说用了批处理就一定比不用批处理要快,这根数据的版本、驱动的版本等都有很大关系。所以实际要用的时候,应该在环境上先测试一下两者的效果。
在上面的测试中 jdbc for循环处理stmt和使用批处理 基本毫无差别。
当然 数据库的连接和断开时非常费时间的,不管有没有用批处理,一定不能for循环拿到数据库的连接和断开 这些IO的操作还是很费时间的
下面是JAVA的API截图下 解释为何设置setAutoCommit
setAutoCommit
void setAutoCommit(boolean autoCommit) throws SQLException
-
将此连接的自动提交模式设置为给定状态。如果连接处于自动提交模式下,则它的所有 SQL 语句将被执行并作为单个事务提交。否则,它的 SQL 语句将聚集到事务中,直到调用
commit
方法或rollback
方法为止。默认情况下,新连接处于自动提交模式。提交发生在语句完成时。语句完成的时间取决于 SQL 语句的类型:
- 对于 DML 语句(比如 Insert、Update 或 Delete)和 DDL 语句,语句在执行完毕时完成。
- 对于 Select 语句,语句在关联结果集关闭时完成。
- 对于
CallableStatement
对象或者返回多个结果的语句,语句在所有关联结果集关闭并且已获得所有更新计数和输出参数时完成。
注:如果在事务和自动提交模式更改期间调用此方法,则提交该事务。如果调用
setAutoCommit
而自动提交模式未更改,则该调用无操作(no-op)。 -
-
- 参数:
-
autoCommit
- 为true
表示启用自动提交模式;为false
表示禁用自动提交模式 - 抛出:
-
SQLException
- 如果发生数据库访问错误,在参与分布式事务的同时调用 setAutoCommit(true),或者在关闭的连接上调用此方法 - 另请参见:
-
getAutoCommit()
上面讲了最基础的JDBC进行批处理的方式,在实际开发的时候我们都是用Mybatis框架,下面讲一讲 Mybatis如何进行批处理
场景:比如我们工作中做导入功能的时候,客户一个excel一千条员工的信息。他们想把当前一万个员工的信息,覆盖到系统中。这种时候不能一条一条的update,就需要使用批处理了
下面上代码,并和JDBC对比做解析。