Mybatis 批量插入


一、首先對於批量數據的插入有兩種解決方案(下面內容只討論和Mysql交互的情況)

1)for循環調用Dao中的單條插入方法

2)傳一個List<Object>參數,使用Mybatis的批量插入 (foreach)

對於批量插入它的Mapper看起來向這樣


    
    
   
   
           
  1. <insert id="addUser" parameterType="java.util.List" >
  2. insert into user(name,age) values
  3. <foreach collection="list" item="item" index="index" separator=",">
  4. (#{item.name},#{item.age})
  5. </foreach>
  6. </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批量插入的另外一種不推薦的寫法


    
    
   
   
           
  1. <insert id="addBatchUser" parameterType="java.util.List" >
  2. <foreach collection="list" item="item" index="index" separator=";">
  3. insert into user(name,age) values(#{item.name},#{item.age})
  4. </foreach>
  5. </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://blog.csdn.net/weixin_34149796/article/details/92362088?utm_medium=distribute.wap_relevant.none-task-blog-title-6

https://www.cnblogs.com/billyxp/p/3631242.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM