一、首先對於批量數據的插入有兩種解決方案(下面內容只討論和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
https://www.cnblogs.com/billyxp/p/3631242.html