Mybatis中的动态SQL在xml中支持的几种常用标签
mybatis3之后,采用了功能强大的OGNL表达式语言消除了许多其它繁琐的标签,现在动态SQL在xml中支持的几种标签:
- if
- choose(when、otherwise)
- trim(where、set)
- foreach
- bind
一.if标签
if标签通常用于where语句中,通过判断参数值来决定是否使用某个查询条件,它也经常用于UPDATE语句中判断是否更新某一个字段,还可以再INSERT语句中用来判断是否插入某个字段的值。
1.1 在where条件中使用if
用法:含义就是当if满足时,就执行标签体中的内容。
需求:准备如下数据表,当只输入用户名的时候,就根据这个用户名进行模糊查询匹配的用户;当只输入邮箱时,就根据邮箱完全匹配用户胡;当两者都同时输入时,就需要用这两个条件去查询匹配的用户。
表格:
贴下关键代码:
(1)controller
@PostMapping("/selectByUser") public List<SysUser> selectByUser(){ SysUser sysUser=new SysUser(); sysUser.setUserName("tes"); return userService.selectByUser(sysUser); }
说明:这里只传入了userName。而userEmail为null
(2)service实现类
1 @Override 2 public List<SysUser> selectByUser(SysUser sysUser) { 3 return userMapper.selectByUser(sysUser); 4 }
(3)mapper接口
List<SysUser> selectByUser(SysUser sysUser);
(4)mapper.xm<select id="selectByUser" resultType="com.example.demo.dao.SysUser"> select id, user_name userName,
user_password userPassword, user_email userEmail, user_info userInfo, head_img headImg, created_time createTime from sys_user where 1=1 <if test="userName!=null and userName!=''"> and user_name like concat('%',#{userName},'%') </if> <if test="userEmail!=null and userEmail!=''"> and user_email=#{userEmail} </if> </select>
说明:1.if标签内有一个必填的属性test,test的值是一个符合OGNL要求的判断表达式,表达式的结果可以认为是ture或者false.
2.当有多个判断条件时,可以使用and或者or进行搭配。
3.上面两个if,它们是平级的关系,只要有满足if条件的,if标签内的 and user_x=... 这一句就会接在where语句后面。如果不满足,就不会有。
4.这里写了一个where 1=1 是为了保证当2个if语句都没有的时候,不会直接以where结束,这样sql语句就会报错。
这种写法where1=1不美观,后面可以采用where标签来拯救它。
(5)实体类
1 package com.example.demo.dao; 2 3 4 import java.sql.Date; 5 6 public class SysUser { 7 /** 8 * 用户ID 9 */ 10 private Long id; 11 /** 12 * 用户名 13 */ 14 private String userName; 15 /** 16 * 密码 17 */ 18 private String userPassword; 19 /** 20 * 邮箱 21 */ 22 private String userEmail; 23 /** 24 * 简介 25 */ 26 private String userInfo; 27 /** 28 * 头像 29 */ 30 private byte[] headImg; 31 /** 32 * 创建时间 33 */ 34 private Date createTime; 35 36 public Long getId() { 37 return id; 38 } 39 40 public void setId(Long id) { 41 this.id = id; 42 } 43 44 public String getUserName() { 45 return userName; 46 } 47 48 public void setUserName(String userName) { 49 this.userName = userName; 50 } 51 52 public String getUserPassword() { 53 return userPassword; 54 } 55 56 public void setUserPassword(String userPassword) { 57 this.userPassword = userPassword; 58 } 59 60 public String getUserEmail() { 61 return userEmail; 62 } 63 64 public void setUserEmail(String userEmail) { 65 this.userEmail = userEmail; 66 } 67 68 public String getUserInfo() { 69 return userInfo; 70 } 71 72 public void setUserInfo(String userInfo) { 73 this.userInfo = userInfo; 74 } 75 76 public byte[] getHeadImg() { 77 return headImg; 78 } 79 80 public void setHeadImg(byte[] headImg) { 81 this.headImg = headImg; 82 } 83 84 public Date getCreateTime() { 85 return createTime; 86 } 87 88 public void setCreateTime(Date createTime) { 89 this.createTime = createTime; 90 } 91 }
采用postman模拟请求,会得到三条和名字模糊匹配的记录:
1 [ 2 { 3 "id": 3, 4 "userName": "test1", 5 "userPassword": "123456", 6 "userEmail": "test1@mybatis.tk", 7 "userInfo": "test1", 8 "headImg": null, 9 "createTime": "2020-03-04" 10 }, 11 { 12 "id": 1001, 13 "userName": "test", 14 "userPassword": "123456", 15 "userEmail": "test@mybatis.tk", 16 "userInfo": "管理员", 17 "headImg": null, 18 "createTime": "2020-03-19" 19 }, 20 { 21 "id": 1002, 22 "userName": "test1", 23 "userPassword": "123456", 24 "userEmail": "test1@mybatis.tk", 25 "userInfo": "test1", 26 "headImg": null, 27 "createTime": "2020-03-25" 28 }, 29 { 30 "id": 1003, 31 "userName": "test1", 32 "userPassword": "123456", 33 "userEmail": "test1@mybatis.tk", 34 "userInfo": "test1", 35 "headImg": null, 36 "createTime": "2020-03-29" 37 }, 38 { 39 "id": 1004, 40 "userName": "test1", 41 "userPassword": "123456", 42 "userEmail": "test1@mybatis.tk", 43 "userInfo": "test1", 44 "headImg": null, 45 "createTime": "2020-03-29" 46 } 47 ]
1.2 在UPDATE更新列中使用if标签
需求:只更新发生变化的字段。
(1)controller
1 @PostMapping("/updateBySelective") 2 int updateBySelective(SysUser sysUser){ 3 sysUser.setUserName("hello"); 4 sysUser.setId(1L); 5 return userService.updateBySelective(sysUser); 6 }
(2)service实现类
1 @Override 2 public int updateBySelective(SysUser sysUser) { 3 return userMapper.updateBySelective(sysUser); 4 }
(3)mapper接口
1 int updateBySelective(SysUser sysUser);
(4)mapper.xml
<update id="updateBySelective"> update sys_user set <if test="userName!=null and userName!=''"> user_name=#{userName}, </if> <if test="userPassword!=null and userPassword!=''"> user_password=#{userPassword}, </if> <if test="userEmail!=null and userEmail!=''"> user_email=#{userEmail}, </if> <if test="userInfo!=null and userInfo!=''"> user_info=#{userInfo}, </if> <if test="headImg!=null"> head_img=#{headImg,jdbcType=BLOB}, </if> <if test="createTime!=null"> created_time=#{createTime,jdbcType=TIMESTAMP}, </if> id=#{id} where id=#{id} </update>
说明:1.这里 id=#{id}是为了防止所有if都不满足的时候语法错误。即使if中有个别满足的,也不能省去,否则sql语法错误。
这种写法不美观,仍然然可以通过where标签和set标签来解决。
1.3 在INSERT动态插入列中使用if标签
需求:如果某一列的参数值不为空,就使用传入的值;如果传入参数为空,就使用数据库中默认的值(通常为空),而不使用传入的空值。
(1)controller
1 @PostMapping("/insertUser") 2 public int insertUser(){ 3 4 SysUser sysUser=new SysUser(); 5 sysUser.setUserName("Tom"); 6 sysUser.setUserEmail("aa.@qq.com"); 7 sysUser.setUserInfo("作家"); 8 return userService.insertUser(sysUser); 9 }
(2)service实现类
1 @Override 2 public int insertUser(SysUser sysUser) { 3 return userMapper.insertUser(sysUser); 4 }
(3)mapper接口
int insertUser(SysUser sysUser);
(4)mapper.xml
<insert id="insertUser"> insert into sys_user( user_name,user_password,user_email, <if test="userInfo!=null and userInfo!=''"> user_info, </if> head_img,created_time) values( #{userName},#{userPassword},#{userEmail}, <if test="userInfo!=null and userInfo!=''"> #{userInfo}, </if> #{headImg,jdbcType=BLOB},#{createTime,jdbcType=TIMESTAMP}) </insert>
二. choose(when、otherwise)
if标签只能实现if功能,没有else功能。于是可以通过本节的组合来实现if..else的功能。choose中农包含when和otherwise两个标签,一个choose中至少有一个when,有0个或1个otherwise.
我的理解:when就相当于java中的else if,而otherwise就相当于java中的else。
需求:准备如下表。当ID有值的时候就优先采用ID进行查询匹配用户;如果ID没有值,那就采用用户名进行查询匹配的用户。
(1)controller
1 @PostMapping("/selectByIdOrName") 2 SysUser selectByIdOrName(){ 3 SysUser sysUser=new SysUser(); 4 sysUser.setId(1L); 5 sysUser.setUserName("test3"); 6 return userService.selectByIdOrName(sysUser); 7 }
(2)service实现类
1 @Override 2 public SysUser selectByIdOrName(SysUser sysUser) { 3 return userMapper.selectByIdOrName(sysUser); 4 }
(3)mapper接口
SysUser selectByIdOrName(SysUser sysUser);
(4)mapper.xml
<select id="selectByIdOrName" resultType="com.example.demo.dao.SysUser"> select id, user_name userName, user_password userPassword, user_email userEmail, user_info userInfo, head_img headImg, created_time createTime from sys_user where 1=1 <choose> <when test="id!=null"> and id=#{id} </when> <when test="userName!=null and userName!=''"> and user_name=#{userName} </when> <otherwise> and 1=2 </otherwise> </choose> </select>
{
"id": 1,
"userName": "test1",
"userPassword": "123456",
"userEmail": "admin@mybatis.tk",
"userInfo": "管理员",
"headImg": null,
"createTime": "2020-03-01"
}
三. where、set、trim
3.1 where标签
修改之前的where 1=1例子,姓名模糊匹配,邮箱完全匹配查询。
修改之前:
<select id="selectByUser" resultType="com.example.demo.dao.SysUser"> select id, user_name userName, user_password userPassword, user_email userEmail, user_info userInfo, head_img headImg, created_time createTime from sys_user where 1=1 <if test="userName!=null and userName!=''"> and user_name like concat('%',#{userName},'%') </if> <if test="userEmail!=null and userEmail!=''"> and user_email=#{userEmail} </if> </select>
直接将xml文件改为如下:
<select id="selectByUser" resultType="com.example.demo.dao.SysUser"> select id, user_name userName, user_password userPassword, user_email userEmail, user_info userInfo, head_img headImg, created_time createTime from sys_user <where> <if test="userName!=null and userName!=''"> and user_name like concat('%',#{userName},'%') </if> <if test="userEmail!=null and userEmail!=''"> and user_email=#{userEmail} </if> </where> </select>
说明:当if都不满足的时候,where中就没有元素,就不会存在之前缺少where 1=1造成sql错误的问题。
3.2 set用法
还是之前的例子:
修改之前:
<update id="updateBySelective"> update sys_user set <if test="userName!=null and userName!=''"> user_name=#{userName}, </if> <if test="userPassword!=null and userPassword!=''"> user_password=#{userPassword}, </if> <if test="userEmail!=null and userEmail!=''"> user_email=#{userEmail}, </if> <if test="userInfo!=null and userInfo!=''"> user_info=#{userInfo}, </if> <if test="headImg!=null"> head_img=#{headImg,jdbcType=BLOB}, </if> <if test="createTime!=null"> created_time=#{createTime,jdbcType=TIMESTAMP}, </if> id=#{id} where id=#{id} </update>
修改之后:
1 <update id="updateUser"> 2 update sys_user 3 set 4 <foreach collection="_parameter" item="val" index="key" separator=","> 5 ${key}=#{val} 6 </foreach> 7 where id=#{id} 8 </update> 9 10 <update id="updateBySelective"> 11 update sys_user 12 <set> 13 <if test="userName!=null and userName!=''"> 14 user_name=#{userName}, 15 </if> 16 <if test="userPassword!=null and userPassword!=''"> 17 user_password=#{userPassword}, 18 </if> 19 <if test="userEmail!=null and userEmail!=''"> 20 user_email=#{userEmail}, 21 </if> 22 <if test="userInfo!=null and userInfo!=''"> 23 user_info=#{userInfo}, 24 </if> 25 <if test="headImg!=null"> 26 head_img=#{headImg,jdbcType=BLOB}, 27 </if> 28 <if test="createTime!=null"> 29 created_time=#{createTime,jdbcType=TIMESTAMP}, 30 </if> 31 id=#{id} 32 </set> 33 where id=#{id} 34 </update>
这种只解决了逗号问题,不是很全面的解决问题,仍然需要
id=#{id}
3.3 trim标签
where和set标签的功能都能通过trim标签实现。
举例:
(1)还是之前的用户名模糊匹配,邮箱完全匹配的例子:(使用trim标签去除多余的and关键字)
修改mapper.xml文件:
<select id="selectByUser" resultType="com.example.demo.dao.SysUser"> select id, user_name userName, user_password userPassword, user_email userEmail, user_info userInfo, head_img headImg, created_time createTime from sys_user <trim prefix="where" prefixOverrides="and"> <if test="userName!=null and userName!=''"> and user_name like concat('%',#{userName},'%') </if> <if test="userEmail!=null and userEmail!=''"> and user_email=#{userEmail} </if> </trim> </select>
效果一样的。但是就不会多写where1=1,并且指定了prefix为where。