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(); }
無語了。。。