1. 疑问
- 问题一:Statement的executeBatch方法是否会执行commit操作,是否还需要再执行一次commit()?
- 问题二:执行批量操作的过程中,如果其中有部分命令执行失败,其他执行成功的命令是否会提交到数据库?
2. 环境
测试采用MySQL数据库,创建如下表;
CREATE TABLE `batch_test` (`id` int(11) NOT NULL ,PRIMARY KEY (`id`))
3. 测试
3.1. 问题一测试
测试代码:
Connection conn = null;Statement stmt = null;try { conn = DBUtils.getConnection(); conn.setAutoCommit(false); stmt = conn.createStatement(); stmt.addBatch("INSERT INTO batch_test(id) VALUES (1)"); stmt.addBatch("INSERT INTO batch_test(id) VALUES (2)"); stmt.addBatch("INSERT INTO batch_test(id) VALUES (3)"); stmt.executeBatch();} catch (SQLException e) { e.printStackTrace();} finally { DBUtils.close(conn, stmt);}
结果:
mysql> select * from batch_test;Empty set
修改代码,增加commit()
Connection conn = null;Statement stmt = null;try { conn = DBUtils.getConnection(); conn.setAutoCommit(false); stmt = conn.createStatement(); stmt.addBatch("INSERT INTO batch_test(id) VALUES (1)"); stmt.addBatch("INSERT INTO batch_test(id) VALUES (2)"); stmt.addBatch("INSERT INTO batch_test(id) VALUES (3)"); stmt.executeBatch(); // ************ conn.commit();} catch (SQLException e) { e.printStackTrace();} finally { DBUtils.close(conn, stmt);}
结果:
mysql> select * from batch_test;+----+| id |+----+| 1 || 2 || 3 |+----+3 rows in set
对比上面两个测试,说明在执行executeBatch()后必须再执行commit(), executeBatch不会执行commit操作;
3.2. 问题二测试
接着3.1的测试数据,目前表batch_test中已经有三条记录,id字段是主键,并且已经存在三个值(1,2,3),我再依次添加4条insert命令,id值顺序为(4,5,1,6),当执行executeBatch时第三条记录(id=1)应该会失败,且抛出异常。
代码如下:
try { conn = DBUtils.getConnection(); conn.setAutoCommit(false); stmt = conn.createStatement(); stmt.addBatch("INSERT INTO batch_test(id) VALUES (4)"); stmt.addBatch("INSERT INTO batch_test(id) VALUES (5)"); stmt.addBatch("INSERT INTO batch_test(id) VALUES (1)"); stmt.addBatch("INSERT INTO batch_test(id) VALUES (6)"); stmt.executeBatch(); System.out.println("executeBatch"); conn.commit(); System.out.println("commit");} catch (BatchUpdateException e) { e.printStackTrace(); System.out.println("UpdateCounts -> " + Arrays.toString(e.getUpdateCounts()));} catch (SQLException e) { e.printStackTrace();} finally { DBUtils.close(conn, stmt);}
执行executeBatch()时抛异常BatchUpdateException,输出:
java.sql.BatchUpdateException: Duplicate entry '1' for key 'PRIMARY'
at com.mysql.jdbc.StatementImpl.executeBatch(StatementImpl.java:1110) at com.tiza.test.db.BatchExecTest.main(BatchExecTest.java:26) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144) UpdateCounts -> [1, 1, -3, 1]
结果:
mysql> select * from batch_test;+----+| id |+----+| 1 || 2 || 3 |+----+3 rows in set
根据结果可知,批量操作当出现一条SQL命令失败,会抛异常,并且UpdateCounts显示,除了id=1的记录执行失败,其他三条都成功了。 但是这里抛异常后就没有执行commit,那如果执行commit是不是三条成功的(id=4,id=5,id=6)就会提交到数据库?
进一步测试,在抛BatchUpdateException后依然做commit,代码如下:
Connection conn = null;Statement stmt = null;try { conn = DBUtils.getConnection(); conn.setAutoCommit(false); stmt = conn.createStatement(); stmt.addBatch("INSERT INTO batch_test(id) VALUES (4)"); stmt.addBatch("INSERT INTO batch_test(id) VALUES (5)"); stmt.addBatch("INSERT INTO batch_test(id) VALUES (1)"); stmt.addBatch("INSERT INTO batch_test(id) VALUES (6)"); stmt.executeBatch(); System.out.println("executeBatch"); conn.commit(); System.out.println("commit");} catch (BatchUpdateException e) { e.printStackTrace(); System.out.println("UpdateCounts -> " + Arrays.toString(e.getUpdateCounts())); // ********************** try { conn.commit(); } catch (SQLException e1) { e1.printStackTrace(); } // **********************} catch (SQLException e) { e.printStackTrace();} finally { DBUtils.close(conn, stmt);}
输出:
java.sql.BatchUpdateException: Duplicate entry '1' for key 'PRIMARY'
at com.mysql.jdbc.StatementImpl.executeBatch(StatementImpl.java:1110) at com.tiza.test.db.BatchExecTest.main(BatchExecTest.java:26) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144) UpdateCounts -> [1, 1, -3, 1]
结果:
mysql> select * from batch_test;+----+| id |+----+| 1 || 2 || 3 || 4 || 5 || 6 |+----+6 rows in set
至此问题二答案已经出来了,批量提交过程中虽然在执行executeBatch时当部分命令出现异常,但是只要继续执行commit,其中成功执行的命令还是会commit到数据库的。
4. 总结
问题一:Statement的executeBatch方法是否会执行commit操作,是否还需要再执行一次commit()?
答: executeBatch不会执行commit,在执行完executeBatch后必须再执行commit;
问题二:执行批量操作的过程中,如果其中有部分命令执行失败,其他执行成功的命令是否会提交到数据库?
答: 批量操作执行executeBatch时部分命令执行失败会抛BatchUpdateException异常,但是只要继续执行commit,其他成功执行的命令依然会提交到数据库,否则不关执行成功与否都不会提交到数据库。