动态 SQL
MyBatis 的强大特性之一便是它的动态 SQL 。
- if
- choose (when, otherwise)
- trim (where, set)
- foreach
if标签
如果布尔值为true,则拼接中的内容,否则就不拼接中内容
<select id="getUserByUser2" resultType="com.zm.entity.User" parameterType="com.zm.entity.User">
select id,name,password,age FROM tb_users
where id=#{id}
<if test=" name != null">
and name = #{name}
</if>
</select>
@Test
public void test18() {
SqlSession session = sqlSessionFactory.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = new User();
user.setId(1L);
//user.setName("张三");
User u = mapper.getUserByUser2(user);
System.out.println(u);
}
sql输出 当有name时sql中会自动拼接 and name = ?
--有name时:
DEBUG [main] - ==> Preparing: select id,name,password,age FROM tb_users where id=? and name = ?
DEBUG [main] - ==> Parameters: 1(Long), 张三(String)
DEBUG [main] - <== Total: 1
User{id=1, name='张三', password='123456', age=24}
--无name时:
DEBUG [main] - ==> Preparing: select id,name,password,age FROM tb_users where id=?
DEBUG [main] - ==> Parameters: 1(Long)
DEBUG [main] - <== Total: 1
User{id=1, name='张三', password='123456', age=24}
choose, when, otherwise标签
choose选择语句,类似java语句中的switch语句。通过创建多个标签元素来选择符合要求的元素。when表示在满足某种情况下调用该内容,otherwise表示在所有情况都不满足的情况下调用的内容。
<select id="getUserByUser3" resultType="com.zm.entity.User" parameterType="com.zm.entity.User">
select id,name,password,age FROM tb_users
where id=#{id}
<choose>
<when test="name != null ">
and name = #{name}
</when>
<when test=" password!=null ">
and password = #{password}
</when>
<otherwise>
and 1 = 1
</otherwise>
</choose>
</select>
@Test
public void test19() {
SqlSession session = sqlSessionFactory.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = new User();
user.setId(1L);
//user.setName("张三");
User u = mapper.getUserByUser3(user);
System.out.println(u);
}
sql输出:当name有值时,拼接sql,当条件都不满足时,就拼接otherwise中sql
--name有值时
DEBUG [main] - ==> Preparing: select id,name,password,age FROM tb_users where id=? and name = ?
DEBUG [main] - ==> Parameters: 1(Long), 张三(String)
DEBUG [main] - <== Total: 1
User{id=1, name='张三', password='123456', age=24}
--name没有值时
DEBUG [main] - ==> Preparing: select id,name,password,age FROM tb_users where id=? and 1 = 1
DEBUG [main] - ==> Parameters: 1(Long)
DEBUG [main] - <== Total: 1
User{id=1, name='张三', password='123456', age=24}
where标签
where 元素只会在至少有一个子元素的条件返回 SQL 子句的情况下才去插入“WHERE”子句。而且,若语句的开头为“AND”或“OR”,where 元素也会将它们去除。
<select id="getUserByUser4" resultType="com.zm.entity.User" parameterType="com.zm.entity.User">
select id,name,password,age FROM tb_users
<where>
<if test=" id!=null">
id = #{id}
</if>
<if test=" name != null">
and name = #{name}
</if>
</where>
</select>
@Test
public void test20() {
SqlSession session = sqlSessionFactory.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = new User();
user.setId(1L);
user.setName("张三");
User u = mapper.getUserByUser4(user);
System.out.println(u);
}
--当 id和name都有值时:
DEBUG [main] - ==> Preparing: select id,name,password,age FROM tb_users WHERE id = ? and name = ? limit 1
DEBUG [main] - ==> Parameters: 1(Long), 张三(String)
DEBUG [main] - <== Total: 1
User{id=1, name='张三', password='123456', age=24}
--当 id有值,name无值时:
DEBUG [main] - ==> Preparing: select id,name,password,age FROM tb_users WHERE id = ? limit 1
DEBUG [main] - ==> Parameters: 1(Long)
DEBUG [main] - <== Total: 1
User{id=1, name='张三', password='123456', age=24}
-- 当id无值,name有值时:
DEBUG [main] - ==> Preparing: select id,name,password,age FROM tb_users WHERE name = ? limit 1
DEBUG [main] - ==> Parameters: 张三(String)
DEBUG [main] - <== Total: 1
User{id=1, name='张三', password='123456', age=24}
--当id和name都无值时:
DEBUG [main] - ==> Preparing: select id,name,password,age FROM tb_users limit 1
DEBUG [main] - ==> Parameters:
DEBUG [main] - <== Total: 1
User{id=1, name='张三', password='123456', age=24}
set标签
set标签用于update语句中, set 元素会动态前置 SET 关键字,同时也会删掉无关的逗号,因为用了条件语句之后很可能就会在生成的 SQL 语句的后面留下这些逗号。(译者注:因为用的是“if”元素,若最后一个“if”没有匹配上而前面的匹配上,SQL 语句的最后就会有一个逗号遗留)
<update id="updateUser1" parameterType="com.zm.entity.User">
update tb_users
<set>
<if test=" name!=null">name = #{name},</if>
<if test=" age!=null">age = #{age}</if>
</set>
where id=#{id}
</update>
trim标签
trim标签是一个格式化标签,可以用来代替set或者where标签的功能
<update id="updateUser2" parameterType="com.zm.entity.User">
update tb_users
<!-- prefix表示调用SQL语句时在最前面添加的前缀
prefixOverrides表示调用SQL语句时去掉的第一个指定内容
suffix表示调用SQL语句时在最后面添加的后缀
suffixOverride表示调用SQL语句时去掉的最后一个指定内容
-->
<trim prefix="set" prefixOverrides="and | or" suffix="" suffixOverrides=",">
<if test=" name!=null">name = #{name},</if>
<if test=" age!=null">age = #{age}</if>
</trim>
where id=#{id}
</update>
@Test
public void test22() {
SqlSession session = sqlSessionFactory.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = new User();
user.setId(1L);
user.setName("张三1");
int u = mapper.updateUser2(user);
}
--当name有值,age没有值时
DEBUG [main] - ==> Preparing: update tb_users set name = ? where id=?
DEBUG [main] - ==> Parameters: 张三1(String), 1(Long)
DEBUG [main] - <== Updates: 1
--当name有值,age没有值时 去掉suffixOverrides="," sql中还保留了“,”,sql报错
DEBUG [main] - ==> Preparing: update tb_users set name = ?, where id=?
DEBUG [main] - ==> Parameters: 张三1(String), 1(Long)
foreach标签
foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及在迭代结果之间放置分隔符 。
可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象传递给 foreach 作为集合参数。当使用可迭代对象或者数组时,index 是当前迭代的次数,item 的值是本次迭代获取的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。
foreach元素的属性主要有item,index,collection,open,separator,close。
- **item:**集合中元素迭代时的别名,该参数为必选。
- index:在list和数组中,index是元素的序号,在map中,index是元素的key,该参数可选
- open:foreach代码的开始符号,一般是(和close=")"合用。常用在in(),values()时。该参数可选
- separator:元素之间的分隔符,例如在in()的时候,separator=","会自动在元素中间用“,“隔开,避免手动输入逗号导致sql错误,如in(1,2,)这样。该参数可选。
- close: foreach代码的关闭符号,一般是)和open="("合用。常用在in(),values()时。该参数可选。
- collection: 要做foreach的对象,作为入参时,List对象默认用"list"代替作为键,数组对象有"array"代替作为键,Map对象没有默认的键。当然在作为入参时可以使用@Param(“keyName”)来设置键,设置keyName后,list,array将会失效。 除了入参这种情况外,还有一种作为参数对象的某个字段的时候。举个例子:如果User有属性List ids。入参是User对象,那么这个collection = “ids”.如果User有属性Ids ids;其中Ids是个对象,Ids有个属性List id;入参是User对象,那么collection = "ids.id"
在使用foreach的时候最关键的也是最容易出错的就是collection属性,该属性是必须指定的,但是在不同情况下,该属性的值是不一样的,主要有一下3种情况:
- 如果传入的是单参数且参数类型是一个List的时候,collection属性值为list .
- 如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array .
- 如果传入的参数是多个的时候,我们就需要把它们封装成一个Map了,当然单参数也可以封装成map,实际上如果你在传入参数的时候,在MyBatis里面也是会把它封装成一个Map的,map的key就是参数名,所以这个时候collection属性值就是传入的List或array对象在自己封装的map里面的key.
<select id="getUserByforeach" resultType="com.zm.entity.User" parameterType="list">
select id,name,password,age from
tb_users
where id in
<foreach collection="list" separator="," open="(" close=")" item="item" index="index">
#{item}
</foreach>
</select>
@Test
public void test23() {
SqlSession session = sqlSessionFactory.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
List<Long> list = Arrays.asList(1L, 2L, 3L);
List<User> users = mapper.getUserByforeach(list);
users.forEach( user -> System.out.println(user));
}
DEBUG [main] - ==> Preparing: select id,name,password,age from tb_users where id in ( ? , ? , ? )
DEBUG [main] - ==> Parameters: 1(Long), 2(Long), 3(Long)
DEBUG [main] - <== Total: 3
User{id=1, name='张三', password='123456', age=24}
User{id=2, name='李四', password='234567', age=25}
User{id=3, name='王五', password='345678', age=26}