第一章、动态SQL
MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其它类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句的痛苦。例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。
例如,下面需求就会使用到拼接sql语句:
【需求】:查询男性用户,如果输入了用户名,按用户名模糊查询,如果没有输入用户名,就查询所有男性用户。
正常的sql语句:查询男性并且用户名中包含zhang
select * from tb_user where sex = "男" and user_name like '%zhang%'
select * from tb_user where sex = "男"
实现需求时还要判断用户是否输入用户名来做不同的查询要求,而这里似乎没有办法判断是否输入了用户名,因此可以考虑使用动态sql来完成这个功能。
动态 SQL 元素和后面学习的 JSTL 或基于之前学习的类似 XML 的文本处理器相似。在 MyBatis 之前的版本中,有很多元素需要花时间了解。MyBatis 3 开始精简了元素种类,现在只需学习原来一半的元素便可。MyBatis 采用功能强大的 OGNL 的表达式来淘汰其它大部分元素。
常见标签如下:
if:判断 if(1 gt 2){}
choose (when, otherwise):分支判断 switch:多选一
trim (where, set):去除
foreach:循环遍历标签
动态SQL中的业务逻辑判断需要使用到以下运算符: ognl表达式
1. e1 or e2 满足一个即可
2. e1 and e2 都得满足
3. e1 == e2,e1 eq e2 判断是否相等
4. e1 != e2,e1 neq e2 不相等
5. e1 lt e2:小于 lt表示less than
6. e1 lte e2:小于等于,其他gt(大于),gte(大于等于) gt 表示greater than
7. e1 in e2
8. e1 not in e2
9. e1 + e2,e1 * e2,e1/e2,e1 - e2,e1%e2
10. !e,not e:非,求反
11. e.method(args)调用对象方法
12. e.property对象属性值
13. e1[ e2 ]按索引取值,List,数组和Map
14. @class@method(args)调用类的静态方法
15. @class@field调用类的静态字段值
1、if标签
格式:
<if test="判断条件">满足条件执行的代码</if>
说明:1)if标签:判断语句,用于进行逻辑判断的。如果判断条件为true,则执行if标签的文本内容2)test属性:用来编写表达式,支持ognl;
【需求】:查询男性用户,如果输入了用户名,按用户名模糊查询,如果没有输入用户名,就查询所有男性用户。
正常的sql语句:查询男性并且用户名中包含zhang
select * from tb_user where sex = "男" and user_name like '%zhang%'
select * from tb_user where sex = "男"
实现需求时还要判断用户是否输入用户名来做不同的查询要求,而这里似乎没有办法判断是否输入了用户名,因此可以考虑使用动态sql来完成这个功能。
上述动态sql语句部分: and user_name like ‘%zhang%’
1.1、定义接口方法
在UserMapper接口中,定义如下方法:
/*** 根据用户名模糊查询* @param userName* @return*/List<User> queryLikeUserName(@Param("userName") String userName);
1.2、编写SQL
在UserMapper.xml文件中编写与方法名同名的sql语句:
<select id="queryLikeUserName" resultType="user">select * from user where sex='男'<if test="userName!=null and userName.trim()!=''">and username like '%${userName}%'</if></select>
【注】<if>
判断中:
1、if标签:用来判断;
2、test属性:使用OGNL表达式,完成具体的判断业务逻辑;
3、这里使用的字符串拼接,所以这里不能是#取值,只能使用$取值,否则会报错
1.3、测试
【userName有值】
对应的SQL语句是:select * from user where sex=“男” and username like ‘%孙%’
【userName没有值】
对应的SQL语句是:select * from user where sex=“男”
【小结】
1、if标签:用来在sql中处理判断是否成立的情况;
2、属性:test中书写OGNL表达式,如果结果为true,if标签的文本中的内容会被拼接到SQL中,反之不会被拼接到SQL中;
3、if标签的应用场景:适用于 二选一
2、choose,when,otherwise
choose标签:分支选择(多选一,遇到成立的条件即停止)when子标签:编写条件,不管有多少个when条件,一旦其中一个条件成立,后面的when条件都不执行。test属性:编写ognl表达式otherwise子标签:当所有条件都不满足时,才会执行该条件。
需求:
编写一个查询方法,设置两个参数,一个是用户名,一个是住址。根据用户名或者住址查询所有男性用户:如果输入了用户名则按照用户名模糊查找,否则就按照住址查找,两个条件只能成立一个,如果都不输入就查找用户名为“孙悟空”的用户。
【需求分析】
1、查询所有男性用户,如果输入了用户名则按照用户名模糊查找;
SELECT * FROM user WHERE sex = "男" AND username LIKE '%孙%';
2、查询所有男性用户,如果输入了住址则按照住址查询;
SELECT * FROM user WHERE sex = "男" AND address = "花果山水帘洞";
3、查询所有男性用户,如果都不输入就查找用户名为“孙悟空”的用户。
SELECT * FROM user WHERE sex = "男" AND username = '孙悟空';
2.1、定义接口方法
在UserMapper接口中,定义接口方法:
/*查询用户名或者地址*/List<User> queryByUserNameOrAddress(@Param("userName") String userName, @Param("address") String address);
2.2、编写SQL
在UserMapper.xml中编写对应的SQL语句
<!--根据用户名或者住址查询所有男性用户:如果输入了用户名则按照用户名模糊查找,否则就按照住址查找,两个条件只能成立一个,如果都不输入就查找用户名为“孙悟空”的用户。--><select id="queryByUserNameOrAddress" resultType="user">select * from user where sex='男'<choose><when test="userName!=null and userName.trim()!=''">and username like '%${userName}%'</when><when test="address!=null and address.trim()!=''">and address = #{address}</when><otherwise>and username='孙悟空'</otherwise></choose></select>
2.3、测试
编写测试类,对这个方法进行测试:
@Test
public void queryByUserNameOrAddress(){List<User> userList = userMapper.queryByUserNameOrAddress("", null);System.out.println("userList = " + userList);
}
【小结】
1、choose,when,otherwise标签组合的作用类似于java中的switch语句,使用于多选一;
3、where
where标签:拼接多条件查询时 1、能够添加where关键字; 2、能够去除多余的and或者or关键字
案例:按照如下条件查询所有用户,
如果输入了用户名按照用户名进行查询,
如果输入住址,按住址进行查询,
如果两者都输入,两个条件都要成立。
【需求分析】
1、如果输入了用户名按照用户名进行查询,
SELECT * FROM user WHERE user_name = '孙悟空';
2、如果输入住址,按住址进行查询,
SELECT * FROM user WHERE address='花果山水帘洞';
3、如果两者都输入,两个条件都要成立。
SELECT * FROM user WHERE user_name = '孙悟空' AND address='花果山水帘洞';
3.1、定义接口方法
在UserMapper接口中定义如下方法:
List<User> queryByUserNameAndAge(@Param("userName") String userName, @Param("address") String address);
3.2、编写SQL
在UserMapper.xml中编写SQL
<!--如果输入了用户名按照用户名进行查询,如果输入住址,按住址进行查询,如果两者都输入,两个条件都要成立。说明:如果按照如下写sql语句会有问题,假设用户名username是空,那么用户名的sql语句不参与条件,此时sql语句就会变为:SELECT * FROM user where AND address = #{address}where后面直接书写了and显然不满足sql语句语法,这里会报错我们可以使用where标签解决上述问题:where标签:拼接多条件查询时 1、能够添加where关键字; 2、能够去除多余的and或者or关键字--><!-- <select id="queryByUserNameAndAge" resultType="user">SELECT * FROM user where<if test="userName != null and userName.trim()!=''">username = #{userName}</if><if test="address!=null and address.trim()!=''">AND address = #{address}</if></select>--><!-- SELECT * FROM user WHERE address = ?where子标签将and去掉了--><select id="queryByUserNameAndAge" resultType="user">SELECT * FROM user<where><if test="userName != null and userName.trim()!=''">username = #{userName}</if><if test="address!=null and address.trim()!=''">AND address = #{address}</if></where></select>
说明:
1.说明:如果按照如下写sql语句会有问题,假设用户名username是空,那么用户名的sql语句不参与条件,此时
sql语句就会变为:SELECT * FROM user where AND address = #{address}
where后面直接书写了and显然不满足sql语句语法,这里会报错
我们可以使用where标签解决上述问题:
where标签:拼接多条件查询时 1、能够添加where关键字; 2、能够去除多余的and或者or关键字
2.SELECT * FROM user WHERE address = ?
where子标签将and去掉了
3.3、测试
@Testpublic void queryByUserNameAndAge() {List<User> userList = userMapper.queryByUserNameAndAge("", "花果山水帘洞");System.out.println("userList = " + userList);}
只传入住址,此时where子标签去掉了and.
【小结】
1、<where>标签作用:用于拼接多选一或者同时成立的SQL情况;
2、<where>还会根据情况,动态的去掉SQL语句中的AND或者or;
4、set
set标签:在update语句中,可以自动添加一个set关键字,并且会将动态sql最后多余的逗号去除。
案例:修改用户信息,如果参数user中的某个属性为null,则不修改。
如果在正常编写更新语句时,如下:
update user SET username = ?, birthday=?, sex=?, where id = ?
那么一旦在传递的参数中没有address,此时生成的sql语句就会因为多了一个逗号而报错。
4.1、定义接口方法
在UserMapper接口中定义如下方法:
void updateSelectiveUser(User user);
4.2、编写SQL
在UserMapper.xml文件中编写如下SQL:
<!--选择性地对user数据进行修改--><update id="updateSelectiveUser">update user<set><if test="username != null and username.trim()!=''">username = #{username},</if><if test="birthday != null">birthday=#{birthday},</if><if test="sex != null and sex.trim()!=''">sex=#{sex},</if><if test="address != null and address.trim()!=''">address=#{address}</if></set>where id = #{id}</update>
4.3、测试
@Testpublic void updateSelectiveUser() {User user = new User();user.setUsername("锁哥1");user.setBirthday(new Date());user.setSex("男");user.setAddress("");user.setId(7);userMapper.updateSelectiveUser(user);}
【结果】
update user SET username = ?, birthday=?, sex=? where id = ?
【小结】
1、<set>标签替代了sql语句中的set关键字;
2、<set>标签还能把sql中多余的,去掉;
5、foreach
foreach标签:遍历集合或者数组
<foreach collection="集合名或者数组名" item="元素" separator="标签分隔符" open="以什么开始" close="以什么结束">#{元素}
</foreach>collection属性:接收的集合或者数组,集合名或者数组名item属性:集合或者数组参数中的每一个元素 separator属性:标签分隔符 open属性:以什么开始 close属性:以什么结束
需求:按照id值是1,2,3来查询用户数据;
5.1、定义接口方法
在UserMapper接口中定义如下方法:
List<User> queryByIds(@Param("arrIds") Integer[] arrIds);
这里一定加@Param(“arrIds”),否则报错
5.2、编写SQL
<!--根据多个id值查询--><select id="queryByIds" resultType="user">SELECT * FROM user WHERE id IN<foreach collection="arrIds" item="ID" separator="," open="(" close=")">#{ID}</foreach></select>
5.3、测试
@Testpublic void queryByIds() {Integer[] arrIds = {1,2,3};List<User> userList = userMapper.queryByIds(arrIds);System.out.println("userList = " + userList);}
【小结】
<foreach>标签的作用:用于对查询参数进行遍历取值;
6、小结
If标签:条件判断test属性:编写ognl表达式where标签:用于sql动态条件拼接,添加where关键字,可以将动态sql多余的第一个and或者or去除。set标签: 用于更新语句的拼接,添加set关键字,并可以将动态sql中多余的逗号去除foreach标签:用于遍历参数中的数组或者集合collection属性:参数中的数组或者集合item属性:表示数组或者集合中的某个元素,取出数据使用#{item的属性值}separator属性:分隔符open:以什么开始close:以什么结束