Oracle 版本:12c;
一、开始的时候,按 mybatis 正常流程开发
1、建 mapper
<update id="updateBatch" parameterType="java.util.List"> <foreach collection="list" item="item" index="index" open="begin" close=";end;" separator=";"> update tb_test t <set> t.status = #{item.status} </set> where u.id=#{item.id} </foreach> </update>
2、service 调用:
public int updateList(List<MyObject> myList) {
int i = myMapper.updateBatch(myList); return i; }
3、实际结果
myList 中的记录,数据库更新都成功了,但 myMapper 调用返回的结果是 -1。
二、百度同类问题参考
后来,百度搜到一篇相关的帖子:
https://stackoverflow.com/questions/58909833/using-mybatis-3-4-6-for-oracle-batch-update-and-got-the-1-result/58914577#58914577
按作者的意思,应该是可以的,但我实际测试的时候发现,getUpdateCounts()
返回的数组都是 -2,依然不是实际成功的行数。
三、Oracle 官方文档解释
后来,在 Oracle 的文档说明中,发现的原因:
https://docs.oracle.com/cd/E11882_01/java.112/e16548/oraperf.htm#JJDBC28777
其中,有一段说明:
Update Counts in the Oracle Implementation of Standard Batching
If a statement batch is processed successfully, then the integer array, or update counts array, returned by the statement executeBatch
call will always have one element for each operation in the batch. In the Oracle implementation of standard update batching, the values of the array elements are as follows:
-
For a prepared statement batch, it is not possible to know the number of rows affected in the database by each individual statement in the batch. Therefore, all array elements have a value of
-2
. According to the JDBC 2.0 specification, a value of-2
indicates that the operation was successful but the number of rows affected is unknown. -
For a generic statement batch, the array contains the actual update counts indicating the number of rows affected by each operation. The actual update counts can be provided only in the case of generic statements in the Oracle implementation of standard batching.
-
For a callable statement batch, the server always returns the value
1
as the update count, irrespective of the number rows affected by each operation.
In your code, upon successful processing of a batch, you should be prepared to handle either -2
, 1
, or true update counts in the array elements. For a successful batch processing, the array contains either all -2
, 1, or all positive integers.
发现原来是三类 statement 的区别导致:
parpared statement:-2 表示执行成功,但无法返回实际成功行数;
generic statement:返回实际成功行数;
四、最终解决方案
最后,自己用 generic statement 实现批量更新,但已经脱离的 mybatis 框架了:
private int updateList(List<MyObject> myList){
int size = myList.size(); int batchSize = 100; int index = 0; Long affectedRows = 0L; SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); Connection conn = sqlSession.getConnection(); Statement statement = null;
try { statement = conn.createStatement(); conn.setAutoCommit(false); for(MyObject obj: myList){ index++; String sql = "update tb_test set status = " + obj.getStatus + " where id = " + obj.getId(); statement.addBatch(sql); if (index % batchSize == 0 || index == size) { int[] ints = statement.executeBatch(); affectedRows += IntStream.of(ints).sum(); statement.clearBatch(); } } conn.commit(); conn.setAutoCommit(true); } catch (SQLException throwables) { throwables.printStackTrace(); }finally { try { if(statement != null && !statement.isClosed()){ statement.close(); } } catch (SQLException throwables) { throwables.printStackTrace(); } try { if(conn != null && !conn.isClosed()){ conn.close(); } } catch (SQLException throwables) { throwables.printStackTrace(); } } return affectedRows.intValue(); }
无语了。。。