逐条更新
这种方式显然是最简单,也最不容易出错的,即便出错也只是影响到当条出错的数据,而且可以对每条数据都比较可控。
代码
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万次数据库,这个效率是无法承受的)。这时候也许只能考虑其他方案了,比如引入缓存机制等。