之前寫的代碼,批量更新的操作都是使用for循環語句進行集合的遍歷,一次次調用單條更新的接口,代碼既不美觀,性能也不好,就想使用MyBatis在一個方法裏批量更新數據,我一共想到兩種思路,分享一下:
- 第一種就是使用MyBatis的 <foreach> 去循環創建多條完整的 update 語句,然後交由Mysql數據庫一條一條去執行(這個和之前的性能差不多,都是一條一條去更新)
- 第二種就是使用數據庫支持的case when函數,根據不同的條件去更新對應符合條件的數據
我們先來看看第一種方法吧:
1.使用 <foreach> 循環創建多條完整update語句
此處是將需要修改的參數放入一個 List 中,然後遍歷list集合,創建多條 update 語句,最終的結果如下:
1.1 MyBatis 的XML語句
<!--批量更新 -->
<update id="updateListByHouseId" parameterType="java.util.ArrayList">
<foreach close=";" collection="list" index="index" item="item" open="" separator=";">
update rba_house_status
<trim prefix="set" suffixOverrides=",">
last_push_time = now(),
<if test="item.pushStatus != null">push_status = #{item.pushStatus},</if>
<if test="item.createStatus != null">create_status = #{item.createStatus},</if>
<if test="item.houseCreateTime != null">house_create_time = #{item.houseCreateTime},</if>
<if test="item.updateStatus != null">update_status = #{item.updateStatus},</if>
update_time = now(),
<if test="item.auditStatus != null">audit_status = #{item.auditStatus},</if>
<if test="item.auditDesc != null">audit_desc = #{item.auditDesc},</if>
<if test="item.hotelId != null">hotel_id = #{item.hotelId},</if>
</trim>
<where>house_id = #{item.houseId}</where>
</foreach>
</update>
1.2 MyBatis最終生成的SQL
update rba_house_status set last_push_time = now(), push_status = ?, create_status = ?, house_create_time = ?, update_status = ?, update_time = now(), audit_status = ?, audit_desc = ?, hotel_id = ? WHERE house_id = ? ;
update rba_house_status set last_push_time = now(), push_status = ?, create_status = ?, house_create_time = ?, update_status = ?, update_time = now(), audit_status = ?, audit_desc = ?, hotel_id = ? WHERE house_id = ? ;
上腳本中的 “?” 對應參數對象中各個對應字段的值
寫到這裏,本以爲第一種方法就可以執行了,直接去進行測試了,就在這時,服務器跟我鬧彆扭,扭扭捏捏不執行,一直報一個錯誤: multi-statement not allow
一臉懵逼爲啥還不讓執行了?
查了半天資料發現,原來MyBatis想要一次執行多條語句是需要進行額外的配置,配置在數據庫鏈接串裏面,就是在鏈接串最後面加一個: &allowMultiQueries=true ,如下所示:
jdbc:mysql://127.0.0.1:3306/test_db?characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai&allowMultiQueries=true
加完以後信誓旦旦開始了第二次測試執行,按理來說應該到這裏就可以,結果執行還是報錯,難道是我執行的姿勢不對?清除之前編譯的文件,再試,還是不行好吧,繼續google and 百度…
最後歷經千辛萬苦,還是沒結果,看來還是隻能自己找答案了,最後偶然間發現在數據源的配置文件裏有一個 com.alibaba.druid.wall.WallFilter 的配置,這個filter裏面有個一配置項 com.alibaba.druid.wall.WallConfig ,這個配置項裏面有個一 multiStatementAllow ,這個配置默認是false,果斷改掉配置,添加一個 multiStatementAllow 的配置,如下:
<bean id="wall-filter-config" class="com.alibaba.druid.wall.WallConfig" init-method="init">
<property name="dir" value="META-INF/druid/wall/mysql" />
<property name="selectWhereAlwayTrueCheck" value="false" />
<property name="selectHavingAlwayTrueCheck" value="false" />
<property name="multiStatementAllow" value="true" />
</bean>
配置完成,再次執行測試代碼!
下面說一下第二種方式,使用mysql自己的函數執行:
2.使用數據庫支持的case when函數
這種方式比較簡單,就是使用 MyBatis 的 XML 去構建數據庫SQL,然後交由數據庫執行。原理比較簡單,直接上代碼給大家看一下:
2.1 需要構建的數據庫語句
#使用SQL一次批量更新多條記錄
UPDATE rba_house_status
SET
audit_status = (
CASE
WHEN house_id = 100023 then 3
END
),
audit_desc = (
CASE
WHEN house_id = 100010 then '111'
END
),
last_push_time = now()
WHERE house_id IN (100023,100010)
2.2 MyBatis 的xml
<!--批量多字段更新-->
<update id="updateListByHouseId" parameterType="java.util.ArrayList">
update rba_house_status
<trim prefix="set" suffixOverrides=",">
<trim prefix="push_status =(case" suffix="end),">
<foreach collection="list" item="item">
<if test="item.pushStatus != null">
when house_id = #{item.houseId} then #{item.pushStatus}
</if>
</foreach>
</trim>
<trim prefix="create_status =(case" suffix="end),">
<foreach collection="list" item="item">
<if test="item.createStatus != null">
when house_id = #{item.houseId} then #{item.createStatus}
</if>
</foreach>
</trim>
<trim prefix="house_create_time =(case" suffix="end),">
<foreach collection="list" item="item">
<if test="item.houseCreateTime != null">
when house_id = #{item.houseId} then #{item.houseCreateTime}
</if>
</foreach>
</trim>
update_time = now(),
</trim>
<where>
house_id in
<foreach collection="list" item="item" index="index" open="(" separator="," close=")">
#{item.houseId}
</foreach>
</where>
</update>
至此,兩種方式就介紹完畢,大家還有什麼其他方法,歡迎交流!