让我们回忆一下上篇博客中mybatis是怎样发挥它的作用的,主要是三类文件,第一mapper接口,第二xml文件,第三全局配置文件(application.properties),而今天我们就是来简化mybatis的工作的——利用注解替代xml配置文件。
先贴出mapper接口代码
@Mapper
public interface UserMapper { //获取用户名单 public List<User> getUser() throws Exception; //根据id删除用户 public void deleteUser(int id)throws Exception; //新增用户 public void addUser(User user)throws Exception; //修改用户信息 public void updateUser(User user) throws Exception; }
相较于上次的代码新增了一个修改用户信息的接口,然而怎么做才能替代xml呢???
针对以上的增删改查的操作,有四个注解@Select、@Delete、@Insert、@Update,在注解中加上之前在xml中写的sql就行了,所以完整的mapper接口文件如下
@Mapper
public interface UserMapper { //获取用户名单 @Select("select * from user") public List<User> getUser() throws Exception; //根据id删除用户 @Delete("delete from user where id = #{id}") public void deleteUser(int id)throws Exception; //新增用户 @Insert("insert into user(id,username,age)values(#{id},#{username},#{age})") public void addUser(User user)throws Exception; //修改用户信息 @Update("update user set username = #{name} where id = #{id}") public void updateUser(User user) throws Exception; }
剩下的service和controller中的代码很简单,和xml开发中的写法保持一致。需要值得注意的是不要忘记在各个接口和类中类名前的位置加上对应的注解,@Mapper、@Service、@Component等,不然spring是扫描不到的。这里有个小技巧,如果在启动类中加上注解@MapperScan(“com.XX.dao”),即可以省去@Mapper注解
说完了注解的开发我们来看一下这样的需求,如果我们要往数据库中更新一个对象,前台传过来的对象中有几个属性没有赋值,所以我们controller接收时就会将这几个属性置空,而我们需要更新不为空的属性,这时该怎么办??当然可以在service中将这个对象处理后再更新到数据库中,但是其实动态sql就可以直接解决这个问题。首先将问题分个类
if+set/where
例如现在前台将这样一个对象传过来
{
"id":1,
"age":12
}
我控制器的方法为
//更新用户 public String updateUser(User user)throws Exception{ userService.updateUser(user); return "id为"+user.getId()+"的用户更新了"; }
如果我直接更新到后台显然会把这条记录之前的用户名给覆盖为空(在数据库允许该字段为空的情况下),所以看直接来看我的动态sql代码
<update id="updateUser" parameterType="com.fc.pojo.User"> update user set <if test = "username != null"> user.username = #{username}, </if> <if test = "age != 0"> user.age = #{age} </if>
where id = #{id} </update>
这样的sql拼接代码看起来很简单,但是有一个问题,如果age属性为0(前台如果不传age属性的话默认赋值为0)的话,最终sql语句会变成 update user set user.username =?,where id = ?
这样显然也是不对的,所以mybatis为我们提供了<set></set>标签将多余的逗号去掉,所以最终的动态sql变成
<update id="updateUser" parameterType="com.fc.pojo.User"> update user <set> <if test = "username != null"> user.username = #{username}, </if> <if test = "age != 0"> user.age = #{age} </if> </set> where id = #{id} </update>
这样就解决了我们的问题了。同理在select语句中,也有这样的问题
<select id="getUser" parameterType="com.fc.pojo.User" resultType="com.fc.pojo.User"> select * from user where <if test="username != null"> username=#{username} </if> <if test="age!= null"> and age=#{age} </if> </select>
如果age为空的话sql就变成了select * from user where and age=?,所以要加上<where></where>标签,这样改进后的xml就是
<select id="getUser" parameterType="com.fc.pojo.User" resultType="com.fc.pojo.User"> select * from user <where> <if test="username != null"> username=#{username} </if> <if test="age!= null"> and age=#{age} </if> </where> </select>
foreach
我们在开发的时候经常会遇到这样的需求,删除多个对象,前台直接传一个对象id的集合,这个时候常规的做法是对集合处理,将id拿出来一个个删掉,其实也可以将集合放入一个包装类中,直接把包装类作为parameterType传入xml中,在xml中处理id集合。下面是包装类,我加上了数组的方式,不仅是id集合,还有id数组,都可以用这种方法来做
public class UserVo { private List<Integer>idList;//id集合 private int[]idArray; //id数组 public List<Integer> getIdList() { return idList; } public void setIdList(List<Integer> idList) { this.idList = idList; } public int[] getIdArray() { return idArray; } public void setIdArray(int[] idArray) { this.idArray = idArray; } }
而xml文件中要使用foreach标签,在el表达式中和js中好像都有类似的用法,用来做循环操作,需要注意的是collection的值应该和包装类中的属性名保持一致。具体代码如下
<!-- 以id集合删除用户 --> <delete id="deleteByIdList" parameterType="com.fc.pojo.UserVo"> delete from user where id in <foreach item="id" collection="idList" open="(" separator="," close=")"> #{id} </foreach> </delete> <!-- 以id数组删除用户 --> <delete id="deleteByIdArray" parameterType="com.fc.pojo.UserVo"> delete from user where id in <foreach item="id" collection="idArray" open="(" separator="," close=")"> #{id} </foreach> </delete>
然后是Controller代码
//删除id集合 @RequestMapping(value="deleteByIdList",method=RequestMethod.DELETE) public String deleteByIdList(@RequestBody UserVo userVo)throws Exception{ userService.deleteByIdList(userVo); return "id列表里的用户都删掉了"; } //删除id数组 @RequestMapping(value="deleteByIdArray",method=RequestMethod.DELETE) public String deleteByIdArray(@RequestBody UserVo userVo)throws Exception{ userService.deleteByIdArray(userVo); return "id数组里的用户都删掉了"; }
mapper接口和service的代码我就不贴了,套路跟之前都是一样的
最后是用来测试的json,第一个是测数组的,第二个测集合的
{
"idArray":[1,2,3]
}
{
"idList":[1,2,3]
}
sql片段
sql片段就是将一段重复率较高的sql抽取出来,供别的sql调用,这样可以有效地提高代码的复用率,话不多说,比如我们在新增一个用户时需要返回新增用户id,而我们之前的代码中已经有新增用户的sql,重复写一遍显得多此一举了,就可以把插入语句抽取出来供多次调用,代码如下,为了看到效果,我把之前的新增用户的语句也使用调用sql片段的方法来做
<sql id="base_insert_sql" > insert into user(id,username,age)values(#{id},#{username},#{age}) </sql> <!-- 新增用户 --> <insert id="addUser" parameterType="com.fc.pojo.User"> <include refid="base_insert_sql" /> </insert> <!-- 新增用户,返回用户id --> <insert id="addUserWithId" parameterType="com.fc.pojo.User"> <selectKey keyProperty="id" resultType="int"> select LAST_INSERT_ID() </selectKey> <include refid="base_insert_sql" /> </insert>
好了,我在开发中常用到的动态sql就这些了,然后把以上xml文件转为注解的方法我就懒得去做了,有空再补上,最后安利一款测试软件Postman,挺好用的,公司也在用这个,以上的所有代码都是我用PostMan测试通过的,毕竟浏览器也发不了post和delete请求,所以嗯。。。就酱。