说明:遇到一次需要批量修改对象的场景。传递一个对象集合,需要根据对象ID批量修改数据库数据,使用的是MyBatis框架。查了一些资料,总结出两种实现方式。
创建Demo
首先,创建一个简单的Demo;
(User,用户对象)
import lombok.Data;
import java.io.Serializable;
@Data
public class User implements Serializable {
private String id;
private String username;
private String password;
}
(UserController,用户控制器)
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserMapper userMapper;
/**
* 根据ID查询用户
* @param id
* @return
*/
@GetMapping("/getUser/{id}")
public String getUser(@PathVariable("id") String id){
return userMapper.getUser(id).toString();
}
}
(UserMapper,用户数据访问接口)
import com.hezy.pojo.User;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserMapper {
User getUser(String id);
}
数据库数据,tb_user
表
项目启动,测试一下接口,没得问题;
批量更新
创建一个新的接口,用于触发批量更新对象的代码,内容是传递一个List集合对象给Mapper处理;
@GetMapping("/updateUser")
public void updateUser(){
ArrayList<User> users = new ArrayList<>();
User user1 = new User();
user1.setId("1");
user1.setUsername("zhangsan_fix");
User user2 = new User();
user2.setId("2");
user2.setUsername("lisi_fix");
user2.setPassword("654321_fix");
users.add(user1);
users.add(user2);
userMapper.updateUser(users);
}
接下来,重点是Mapper.xml中的Statement要怎么写,继续往下看
方式一
首先,我们可以使用动态SQL,如下:
<update id="updateUser">
<foreach collection="users" item="user" separator=";">
update tb_user
<set>
<if test="user.username != null and user.username != ''">
username = #{user.username},
</if>
<if test="user.password != null and user.password != ''">
password = #{user.password}
</if>
</set>
where id = #{user.id}
</foreach>
</update>
我们把拼接后的SQL打印出来,会发现一个问题。SQL中,分号(;
)表示一条语句的结束,使用上面的方式拼接出来的方式,是多条SQL。
因此产生了一个问题,Mybatis中一个Statement标签中可以有多条SQL吗? 答案是默认不可以,所以上面的代码报错了。需要在数据库连接后面加上&allowMultiQueries=true
配置,如下:
再次执行,修改成功了,这是第一种方式;
查看日志发现,Updates:1,就是说如果需要返回更新的记录条数,那么这种方式返回的更新条数会是1,不能体现出真实数据库发生变化的记录条数;
另外,将多条SQL合成一条执行,有SQL注入的风险
方式二
第二种方式是用一条SQL的方式来实现,如下:
<update id="updateUser">
update tb_user
set
username = case
<foreach collection="users" item="user">
when id = #{user.id}
<choose>
<when test="user.username != null and user.username != ''">then #{user.username}</when>
<otherwise>then username</otherwise>
</choose>
</foreach>
end,
password = case
<foreach collection="users" item="user">
when id = #{user.id}
<choose>
<when test="user.password != null and user.password != ''">then #{user.password}</when>
<otherwise>then password</otherwise>
</choose>
</foreach>
end
where
<foreach collection="users" item="user" separator="or">
id = #{user.id}
</foreach>
</update>
看着有些复杂,拼接后的SQL如下:
update tb_user
set
username = case
when id = '1' then 'zhangsan_fix'
when id = '2' then 'lisi_fix'
end,
password = case
when id = '1' then password
when id = '2' then '654321_fix'
end
where id = '1'
or id = '2';
这种方式不需要加额外的配置,执行测试;
查看数据库,没得问题,批量修改完成了;
总结
本文介绍了Mybatis框架下,批量更新对象的两种方法:
-
方法一:将多条更新语句合成一条执行,需要在数据库链接后面增加配置,不能体现真实修改的记录条数,有SQL注入的风险;
-
方法二:使用case then 关键字实现,SQL复杂,行数多,降低了可阅读性;