MyBatis批量更新

逐条更新

  这种方式显然是最简单,也最不容易出错的,即便出错也只是影响到当条出错的数据,而且可以对每条数据都比较可控。

代码

1 updateBatch(List<MyData> datas){
2     for(MyData data : datas){
3         try{
4             myDataDao.update(data);
5         }
6         catch(Exception e){
7         }
8     }
9 }

mybatis中update的实现

1 <update>
2     update mydata
3     set ...
4     where ...
5 </update>

单字段批量更新

  逐条更新最然简单,但是逐次连接断开数据库效率实在不高,因此诞生了批量更新的方法。

 1 <update id="updateBatch" parameterType="java.util.List">
 2     update mydata_table 
 3     set status=
 4     <foreach collection="list" item="item" index="index" separator=" " open="case ID" close="end">
 5         when #{item.id} then #{item.status}
 6     </foreach>
 7     where id in
 8     <foreach collection="list" index="index" item="item" separator="," open="(" close=")">
 9         #{item.id,jdbcType=BIGINT}
10     </foreach>
11 </update>

  其中when...then...是sql中的"switch" 语法。这里借助mybatis的<foreach>语法来拼凑成了批量更新的sql,上面的意思就是批量更新id在updateBatch参数所传递List中的数据的status字段。也可以使用<trim>实现同样的功能。

 1 <update id="updateBatch" parameterType="java.util.List">
 2     update mydata_table
 3     <trim prefix="set" suffixOverrides=",">
 4         <trim prefix="status =case" suffix="end,">
 5             <foreach collection="list" item="item" index="index">
 6                 when id=#{item.id} then #{item.status}
 7             </foreach>
 8         </trim>
 9     </trim>
10     where id in
11     <foreach collection="list" index="index" item="item" separator="," open="(" close=")">
12         #{item.id,jdbcType=BIGINT}
13     </foreach>
14 </update>

  prefix,suffix 表示在trim标签包裹的部分的前面或者后面添加内容,如果同时有prefixOverrides,suffixOverrides 表示会用prefix,suffix覆盖Overrides中的内容。 

如果只有prefixOverrides,suffixOverrides 表示删除开头的或结尾的xxxOverides指定的内容。

 上述代码转化成sql如下:

1 update mydata_table 
2 set status = 
3 case
4     when id = #{item.id} then #{item.status}
5     ...
6 end
7 where id in (...);

带条件多字段批量更新

 1 <update id="updateBatch" parameterType="java.util.List">
 2     update sys_user
 3     <trim prefix="set" suffixOverrides=",">
 4         <trim prefix="userName = case" suffix="end,">
 5             <foreach collection="list" item="item" index="index">
 6                 <if test="item.userName != null">
 7                     when userId=#{item.userId} then #{item.userName}
 8                 </if>
 9             </foreach>
10         </trim>
11         <trim prefix="userCode = case" suffix="end,">
12             <foreach collection="list" item="item" index="index">
13                 <if test="item.userCode != null or item.userName == null">
14                     when userId=#{item.userId} then #{item.userCode}
15                 </if>
16             </foreach>
17         </trim>
18     </trim>
19     where userId in
20     <foreach collection="list" index="index" item="item" separator="," open="(" close=")">
21         #{item.userId}
22     </foreach>
23 </update>

  这种批量跟心数据库的方式可以在一次数据库连接中更新所有数据,避免了频繁数据库建立和断开连接的开销,可以很大程度的提高数据更新效率。但是这样的问题是如果这个过程中更新出错,将很难知道具体是哪个数据出错,如果使用数据自身的事务保证,那么一旦出错,所有的更新将自动回滚。而且通常这种方式也更容易出错。因此通常的使用的方案是进行折中,也就是一次批量更新一部分(分页进行更新,比如说一共有1000条数据,一次更新100条)。这样可以分担出错的概率,也更容易定位到出错的位置。 当然如果数据量确实很大的时候,这种批量更新也一样会导致更新效率低下(比如说一次更新100条,那如果10亿条数据呢,一样要批量更新1000万次,建立和断开1000万次数据库,这个效率是无法承受的)。这时候也许只能考虑其他方案了,比如引入缓存机制等。

原文地址:https://www.cnblogs.com/guanghe/p/10437404.html