五、怎么执行批处理?
5.1 准备测试数据
👉 创建数据表t_department
CREATE TABLE `t_department` ( `did` int NOT NULL AUTO_INCREMENT COMMENT '部门编号', `dname` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '员工名称', `description` varchar(200) DEFAULT NULL COMMENT '员工简介', PRIMARY KEY (`did`), UNIQUE KEY `dname` (`dname`) ) ENGINE=InnoDB AUTO_INCREMENT=5009 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
5.2 问题演示
👉概述:
批处理就是执行一组sql语句,大多数情况下都是批量执行insert语句。
👉目的:
为了提高效率
👉批处理方法原理:
- 每次设置完 “?” 之后,先不执行,先攒着pst.addBatch();
把要执行sgL先添加到批处理队伍中,在缓存中攒着。 - 等所有的"?"设置完了之后,一口气执行
💡tips:
如果缓冲区溢满的时候,它也会自动执行一次。
如果缓冲区没有溢出,那么会最后一起执行。
❓怎么开启批处理?
MySQL服务器端,默认批处理功能没有开启。需要通过参数告知mysql服务器,开启批处理功能。
- 在url后面再加一个参数
rewriteBatchedStatements=true
//告知mysql服务器开启批处理功能 String url="jdbc:mysql://localhost:3306/0225db?serverTimezone=UTC&rewriteBatchedStatements=true";
PreparedStatement对象调用
A:addBatch()
B:executeBatch()
- 不要把values写错value,因为value效率太低了
案例:给部门表批量添加1000条部门测试信息
👉ps: 部门表由于之前测试过案例程序,自增长量为5009
⚪A.不使用批处理功能插入数据:
代码演示如下:
@Test public void test01() throws SQLException { long start=System.currentTimeMillis(); //开始连接数据库,相当于网络编程中的socket String url="jdbc:mysql://localhost:3306/0225db?serverTimezone=UTC"; Connection root = DriverManager.getConnection(url, "root", "123456"); String sql="INSERT INTO t_department VALUES(null,?,?)";//t_employee中第一个字段是自增工长列,可不用填写 PreparedStatement pst = root.prepareStatement(sql);//把sql语句装进去;相当于通过PreparedStatement对象把sql发送给MySQL服务端 for (int i = 1; i <=1000 ; i++) { pst.setObject(1,"测试部门"+i); pst.setObject(2,"测试部门简介"+i); pst.executeUpdate(); } //释放资源 pst.close(); root.close(); long end=System.currentTimeMillis(); //耗时:100141毫秒 System.out.println("耗时:"+(end-start)); }
⚪B.Java程序开了pst.executeBatch();//执行批处理命令,但是没告知MySQL服务器要开启批处理功能,不起作用,非常慢,必须二者才行。
不信?请看如下代码演示:
@Test //感觉还是很慢 public void test02() throws SQLException { long start=System.currentTimeMillis(); //开始连接数据库,相当于网络编程中的socket String url="jdbc:mysql://localhost:3306/0225db?serverTimezone=UTC"; Connection root = DriverManager.getConnection(url, "root", "123456"); String sql="INSERT INTO t_department VALUES(null,?,?)";//t_employee中第一个字段是自增工长列,可不用填写 PreparedStatement pst = root.prepareStatement(sql);//把sql语句装进去;相当于通过PreparedStatement对象把sql发送给MySQL服务端 for (int i = 1; i <=1000 ; i++) { pst.setObject(1,"测试部门"+i); pst.setObject(2,"测试部门简介"+i); pst.addBatch();//把所有的sql语句放到批处理队伍中,于缓存中先攒一波 } pst.executeBatch();//执行批处理命令 //释放资源 pst.close(); root.close(); long end=System.currentTimeMillis(); //MySQL没有开启批处理功能,耗时:86547 System.out.println("耗时:"+(end-start)); }
⚪ C.使用批处理功能并用values的sql:
代码演示如下:
@Test //sql语句使用values,感觉飞快,坐火箭一样 public void test03() throws SQLException { long start=System.currentTimeMillis(); //开始连接数据库,相当于网络编程中的socket String url="jdbc:mysql://localhost:3306/0225db?serverTimezone=UTC&rewriteBatchedStatements=true"; Connection root = DriverManager.getConnection(url, "root", "123456"); String sql="INSERT INTO t_department VALUES(null,?,?)";//t_employee中第一个字段是自增工长列,可不用填写 PreparedStatement pst = root.prepareStatement(sql);//把sql语句装进去;相当于通过PreparedStatement对象把sql发送给MySQL服务端 for (int i = 1; i <=1000 ; i++) { pst.setObject(1,"测试部门"+i); pst.setObject(2,"测试部门简介"+i); pst.addBatch();//把所有的sql语句放到批处理队伍中,于缓存中先攒一波 } pst.executeBatch();//执行批处理命令 //释放资源 pst.close(); root.close(); long end=System.currentTimeMillis(); //MySQL开启批处理后,耗时:1614毫秒 System.out.println("耗时:"+(end-start)); }
⚪ D.使用批处理但使用value的sql:
代码演示如下:
@Test //其他代码不变,观察sql语句insert 表名 value(值列表)的执行效率 //value效率太低了 public void test04() throws SQLException { long start=System.currentTimeMillis(); //开始连接数据库,相当于网络编程中的socket String url="jdbc:mysql://localhost:3306/0225db?serverTimezone=UTC&rewriteBatchedStatements=true"; Connection root = DriverManager.getConnection(url, "root", "123456"); String sql="INSERT INTO t_department VALUE(null,?,?)";//t_employee中第一个字段是自增工长列,可不用填写 PreparedStatement pst = root.prepareStatement(sql);//把sql语句装进去;相当于通过PreparedStatement对象把sql发送给MySQL服务端 for (int i = 1; i <=1000 ; i++) { pst.setObject(1,"测试部门"+i); pst.setObject(2,"测试部门简介"+i); pst.addBatch();//把所有的sql语句放到批处理队伍中,于缓存中先攒一波 } pst.executeBatch();//执行批处理命令 //释放资源 pst.close(); root.close(); long end=System.currentTimeMillis(); System.out.println("耗时:"+(end-start));//MySQL开启批处理后,耗时:87386 }
👉以上四种方式运行耗时差异图如下所示:
六、怎么执行事务?
6.1 准备测试数据
👉创建数据表t_department
CREATE TABLE `t_department` ( `did` int NOT NULL AUTO_INCREMENT COMMENT '部门编号', `dname` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '员工名称', `description` varchar(200) DEFAULT NULL COMMENT '员工简介', PRIMARY KEY (`did`), UNIQUE KEY `dname` (`dname`) ) ENGINE=InnoDB AUTO_INCREMENT=5009 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
6.2 问题演示
👉如何管理事务?
- mysql默认是自动提交事务,每执行一条语句成功后,自动提交。
需要开启手动提交模式。
Connection连接对象.setAutoCommit(false)
;//取消自动提交模式,开始手动提交模式
- sql执行成功,别忘了提交事务
Connection连接对象.commit();
- sql执行失败,回滚事务
Connection连接对象.rollback();
案例:修改部门表中的 两条记录,故意分开用两个update语句来修改。
然后希望这两条update语句,构成一个事务,要么一起成功,然后提交,要么一起失败,还原回滚。
update t_department set description = ‘xx’ where did = 2;
update t_department set description = ‘yy’ where did = 3;
代码演示如下:
👉 ①故意写错sql2语句的,观察是否回滚
@Test //故意写错sql2语句的,观察是否回滚;观察结果:发生了回滚 public void test01() throws SQLException { //开始连接本机的MySQL服务端 String url="jdbc:mysql://localhost:3306/0225db?serverTimezone=UTC"; Connection root = DriverManager.getConnection(url, "root", "123456"); root.setAutoCommit(false);//不开启这个,更新失败后不能回滚, String sql1="update t_department set description = 'xx' where did = 2"; String sql2="update t_department set description = 'yy' what did = 3"; PreparedStatement pst1 = root.prepareStatement(sql1);//把sql1语句装进PreparedStatement对象中,准备发送 PreparedStatement pst2 = root.prepareStatement(sql2);//把sql2语句装进PreparedStatement对象中,准备发送 try { pst1.executeUpdate();//真正发送sql1语句 pst2.executeUpdate();//真正发送sql2语句 System.out.println("都更新成功了"); root.commit();//提交事务 } catch (SQLException e) { System.out.println("有一个更新失败了"); root.rollback();//回滚事务 } pst1.close(); root.close(); }
💡观察结果: 事务回滚成功
👉②更正之前写错的sql2语句,观察是否提交
@Test //更正之前写错的sql2语句,观察是否提交;观察结果:发生了提交,数据已经更新 public void test02() throws SQLException { //开始连接本机的MySQL服务端 String url="jdbc:mysql://localhost:3306/0225db?serverTimezone=UTC"; Connection root = DriverManager.getConnection(url, "root", "123456"); root.setAutoCommit(false);//不开启这个,更新失败后不能回滚, String sql1="update t_department set description = 'xx' where did = 2"; String sql2="update t_department set description = 'yy' where did = 3"; PreparedStatement pst1 = root.prepareStatement(sql1);//把sql1语句装进PreparedStatement对象中,准备发送 PreparedStatement pst2 = root.prepareStatement(sql2);//把sql2语句装进PreparedStatement对象中,准备发送 try { pst1.executeUpdate();//真正发送sql1语句 pst2.executeUpdate();//真正发送sql2语句 System.out.println("都更新成功了"); root.commit();//提交事务 } catch (SQLException e) { System.out.println("有一个更新失败了"); root.rollback();//回滚事务 } pst1.close(); pst2.close(); //这里习惯上,在连接对象close之前,把连接重新设置为自动提交模式 root.setAutoCommit(true);//虽然在本例中没有意义,但在数据库连接池中,连接会重复使用,在关闭(还给连接池时)需求恢复自动提交模式 root.close(); }
💡观察结果: 事务提交成功