MyBatis批量插入和效能問題
阿新 • • 發佈:2019-02-03
在有批量插入資料庫的需求時,比如插入50條資料,傳統的做法是利用for迴圈50次,每條資料挨個插入。此種方式存在嚴重效率問題,需要頻繁獲取Session,獲取連線。
此處在foreach之前和之後,用了begin和end,測試用的是Oracle,以這種方式批量插入速度回更快。
MyBatis支援了批量插入的配置和語法。如下:
- <insertid="insertByBatch"parameterType="java.util.List">
- BEGIN
- <foreachcollection="list"item="item"separator=";">
- INSERT INTO T_OPT_HANDOVERBILL_DETAIL (ID, HANDOVER_NO, WAYBILL_NO, TRANSPORT_TYPE, HANDOVER_GOODS_QTY,
- HANDOVER_WEIGHT, ACTUAL_WEIGHT,HANDOVER_VOLUME, ACTUAL_VOLUME, NOTES, GOODS_NAME, PACKING, BE_VALUABLE, RECEIVE_ORG_NAME,
- REACH_ORG_NAME,RECEIVER_NAME, DEST_REGION_NAME, GOODS_QTY, WAYBILL_FEE, DECLARATION_VALUE, CURRENCY_CODE, CREATE_TIME,
- ORIG_ORG_CODE, HANDOVER_TYPE, COD_AMOUNT, BE_JOIN_CAR, BE_FAST_GOODS, IS_INIT, WAYBILL_NOTES,TRANSPORT_TYPE_CODE,
- MODIFY_TIME)
- VALUES
- (#{item.id,jdbcType=VARCHAR}, #{item.handoverNO,jdbcType=VARCHAR}, #{item.waybillNO,jdbcType=VARCHAR},
- #{item.transportType,jdbcType=VARCHAR}, #{item.handoverGoodsQty,jdbcType=NUMERIC}, #{item.handoverWeight,jdbcType=NUMERIC},
- #{item.actualWeight,jdbcType
- #{item.notes,jdbcType=VARCHAR}, #{item.goodsName,jdbcType=VARCHAR}, #{item.packing,jdbcType=VARCHAR},
- #{item.beValuable,jdbcType=VARCHAR}, #{item.receiveOrgName,jdbcType=VARCHAR}, #{item.reachOrgName,jdbcType=VARCHAR},
- #{item.receiverName,jdbcType=VARCHAR}, #{item.destRegionName,jdbcType=VARCHAR}, #{item.goodsQty,jdbcType=VARCHAR},
- #{item.waybillFee,jdbcType=NUMERIC}, #{item.declarationValue,jdbcType=NUMERIC}, #{item.currencyCode,jdbcType=VARCHAR},
- #{item.createTime,jdbcType=DATE}, #{item.origOrgCode,jdbcType=VARCHAR}, #{item.handoverType,jdbcType=VARCHAR},
- #{item.codAmount,jdbcType=NUMERIC}, #{item.beJoinCar,jdbcType=VARCHAR}, #{item.beFastGoods,jdbcType=VARCHAR},
- #{item.isInit,jdbcType=VARCHAR}, #{item.waybillNotes,jdbcType=VARCHAR}, #{item.transportTypeCode,jdbcType=VARCHAR},
- #{item.modifyTime,jdbcType=DATE})
- </foreach>
- ;END;
- </insert>
- publicvoid insertByBatch(List<HandOverBillDetailEntity> list) {
- long start = System.currentTimeMillis();
- getSqlSession().insert(NAME_SPACE + ".insertByBatch", list);
- long end = System.currentTimeMillis();
- System.out.println("批量插入耗時:" + (end - start) + "ms");
- }
此處在foreach之前和之後,用了begin和end,測試用的是Oracle,以這種方式批量插入速度回更快。
效能問題:
(1)Mybatis變數繫結不當引發的效能隱患
在value值中,我們用到了mybatis的變數繫結。用的是#{}方式,還有一種方式${}。
預設情況下,使用#{}語法,MyBatis會用PreparedStatement語句當做佔位符,並且安全的設定PreparedStatement引數,可以避免SQL注入漏洞和釣魚漏洞,這個過程中MyBatis會進行必要的安全檢查和轉義。
示例1:用#{}
執行SQL:Select * from emp where name = #{employeeName}
引數:employeeName=>Smith
解析後執行的SQL:Select * from emp where name = ?
示例2:用${}
執行SQL:Select * from emp where name = ${employeeName}
引數:employeeName傳入值為:Smith
解析後執行的SQL:Select * from emp where name =Smith
可以看出,${}方式影響SQL語句的預編譯,Oracle編譯器解析時會進行硬解析,會造成很多臨時遊標變數的繫結,導致資料庫效能下降。所以從安全性和效能的角度出發,能使用#{}的情況下就不要使用${}
(2)一次批量插入過多造成的效能隱患
雖然mybatis和oracle能支援批量插入,但也並不意味著不限插入語句的數量。經過實際的測試和效能驗證,一次批量插入的資料條數很大時,oracle將不支援語句執行,可能直接報錯:程式太大。而且一次插入資料量大,執行完成的耗費時間將以指數級增長。
如下為我在自身機器上做的簡單測試: