Mybatis 批量插入
一、首先對於批量資料的插入有兩種解決方案(下面內容只討論和Mysql互動的情況)
1)for迴圈呼叫Dao中的單條插入方法
2)傳一個List<Object>引數,使用Mybatis的批量插入 (foreach)
對於批量插入它的Mapper看起來向這樣
- <insert id="addUser" parameterType="java.util.List" >
- insert into user(name,age) values
- <foreach collection="list" item="item" index="index" separator
=","> - (#{item.name},#{item.age})
- </foreach>
- </insert>
二、這兩種方案首先在效能上,肯定是批量插入效能好,更加省時間。
原因如下:
- 迴圈插入:需要每次都獲取session,獲取連線,然後將sql 語句發給mysql 去執行(JDBC一般情況下是通過TCP/IP 進行連線和資料庫進行通訊的)。可以看這裡 mysql四種通訊協議
- 批量插入: 批量插入通過foreach 標籤,將多條資料拼接在sql 語句後,一次執行只獲取一次session,提交一條sql語句。減少了程式和資料庫互動的準備時間。
三、但是批量插入有需要注意的地方:
1、首先是返回值
對於普通的單條插入,資料庫的返回值就是 (0/1) 。
對於返回值代表的意思可以認為是
“語句執行返回的資料庫受影響的行數。”
或者是
“此次執行是否成功(0-失敗,1-成功)。”
對應的也就是在Dao層中,對於插入方法的返回值型別的設定有(int/boolean)兩種
對於批量插入的返回值,返回的還是(0/1),而不是統計插入成功幾條,即使你的Dao層方法的返回值型別為int.
這裡的(0/1) 也就代表著,這次批量插入是否成功(0-失敗,1-成功)。
當然你Dao層的返回值還是可以是(int/boolean)
2、對於批量插入中間有一個失敗會怎麼樣
猜想有下面三種情況
a、繼續插入後面的,把失敗的跳過
b、停止插入,但前面的插入成功保持。
c、全部回滾
這裡就直接放結果了。
批量語句,只要有一個失敗,就會全部失敗。資料庫會回滾全部資料。
關於測試過程可以看這篇部落格:mysql批量插入語句執行失敗的話,是部分失敗還是全部失敗
其實也很好理解。
首先我們知道了mybatis <foreache>批量插入,是在程式內拼接sql 語句(拼接成多條同時插入的sql語句),拼接後發給資料庫。
就相當於咱們自己在mysql的命令列中,執行一條多插入的語句。預設情況下 mysql 單條語句是一個事務,這在一個事務範圍內,當中間的sql語句有問題,或者有一個插入失敗,就會觸發事務回滾。同時你也能看到錯誤提示。(命令列執行單條sql的情況)
所以有一個插入不成功肯定全部回滾。
3、批量插入資料量的限制
我這裡就直接放結論,又興趣的可以看這篇部落格有探究過程 :Mybatis 批量插入引發的血案
1)、Mybatis 本身對插入的資料量沒有限制
2)、mysql對語句的長度有限制,預設是 4M
其他資料庫的情況這裡不介紹,可以自行百度。通過上面 “mysql對語句的長度有限制,預設是 4M” 我們可以知道,批量插入資料是有限制的。不能一下把幾萬條資料(就是太大資料量意思)一次性插入。
所以一般情況下我們推薦即使使用批量插入,也要分批次。
每次批次設定多少?需要根據你的插入一條資料的引數量來做度量。因為受限條件是sql語句的長度。
而且分批插入更加合理,對於插入失敗,回滾範圍會縮小很多。
4、為了保證程式健壯性,對空集合引數的校驗
Mybatis並沒有做集合容量的驗證,如果集合引數為空或者size為0則生成的sql可能只有”insert into user(name,age) values”這樣一段或者沒有,所以說,寫批量sql的時候注意在呼叫批量方法的地方加入對容量的驗證。
5、mybatis批量插入的另外一種不推薦的寫法
- <insert id="addBatchUser" parameterType="java.util.List" >
- <foreach collection="list" item="item" index="index" separator=";">
- insert into user(name,age) values(#{item.name},#{item.age})
- </foreach>
- </insert>
這種寫法也能實現批量插入。但是有很多問題。
a、首先這種方式的批量插入也是sql拼接。但是明顯字元長度增加。這就導致每批次可插入的數量減少
b、這種方式執行返回值還是(0、1)是已經嘗試插入的最後一條資料是否成功。由於這種foreach 拼接成的sql語句,是以分號“;”分隔的多條insert語句。這就導致前面的資料項都插入成功了。(預設資料庫的事務處理是單條提交的,出錯前的執行都是一個個單條語句,所以並並沒有回滾資料。)
所以如果你想中間插入失敗回滾的話,需要使用Spring事務,但是還需要注意spring事務是丟擲執行時異常時才會回滾。這種批量插入中間有沒插入成功的是不會丟擲異常的。所以你需要根據返回值判斷手動編碼丟擲異常。
而最上面的那種寫法就不用是用事務,因為他是一條sql語句。
四、補充 :<foreach>中引數的介紹
foreach簡介
foreach的主要用在構建in條件中,它可以在SQL語句中進行迭代一個集合。
foreach元素的屬性主要有 item,index,collection,open,separator,close。
item表示集合中每一個元素進行迭代時的別名,index指定一個名字,用於表示在迭代過程中,每次迭代到的位置,open表示該語句以什麼開始,separator表示在每次進行迭代之間以什麼符號作為分隔 符,close表示以什麼結束,在使用foreach的時候最關鍵的也是最容易出錯的就是collection屬性,該屬性是必須指定的,但是在不同情況 下,該屬性的值是不一樣的,主要有一下3種情況:
- 1.如果傳入的是單引數且引數型別是一個List的時候,collection屬性值為list
- 2.如果傳入的是單引數且引數型別是一個array陣列的時候,collection的屬性值為array
- 3.如果傳入的引數是多個的時候,我們就需要把它們封裝成一個Map了,當然單引數也可以封裝成map
以上內容為自己總結,難免會有疏漏,如有錯誤還請指出。不勝感謝!
轉載於:https://my.oschina.net/zjllovecode/blog/1818716
http://blog.itpub.net/29254281/viewspace-1151785