Mybatis-3.动态SQL语句
回顾第二章的实操会发现在查询大于或小于某个价格的商品时要分成两个方法去写。有没有什么办法让一个方法就可以判断是大于、小于还是大于等于、小于等于等等?有的,那就是动态SQL。
什么是动态SQL
动态SQL可以理解为一种“根据不同条件拼接sql语句”的一种机制。
动态SQL除了可以解决上面提到的问题还可以解决诸多问题,如:
- 如果数据为null则不让这个数据参加sql语句的执行
- 插入多条数据
<where>、<if>标签
相当于where关键字,如果where标签的值是空的,那么就不会生成where关键字,否则会生成 where ...
。(该标签也会自动生成前面的空格)如:
<select ...>
select * from user
<where><!--user和<where>标签之间的空格由<where>生成-->
<if test="id!=0">id=#{id} and</if>
<if test="name!=null">name=#{name}</if>
</where>
</select>
当参数id的值为空,name不为空时,执行sql语句select * from user where name=#{name}
,id不为空,name为空时,执行语句select * from user where id=#{id}
,当两者都不为空执行select * from user where id=#{id} and name=#{name}
,当两者都为空执行select * from user
。
可以通过这两个标签来解决上面提到的比大小问题:
- 创建Mapper方法为
List<Product> selectProductByPrice(String op,double value)
,其中,op为符号,>为匹配大于value的,<为匹配小于value的,>=为匹配大于等于value的,<=为匹配小于等于value的,=为匹配等于value的。 - 修改Mapper映射文件,在文件中添加下面内容:
<select id="selectProductByPrice" resultType="product"> select * from Product <where> <if test='op==">".toString()'> price > #{value} </if> <if test='op=="<".toString()'> price < #{value} </if> <if test='op==">=".toString()'> price >= #{value} </if> <if test='op=="<=".toString()'> price <= #{value} </if> <if test="op=='='.toString()"> price = #{value} </if> </where> </select>
- 编写一个测试类来测试这个方法(略)
if比较字符串报
NumberFormatException
错误可以尝试在字符串后添加.toString()
<set>和<trim>
<where>用来判断,那么<set>就用来更新。用法:
<update ...>
update user
<set>
<if test="username!=null and username!=' ' ">
username=#{username},
</if>
<if test="pwd!=null and pwd!=' '">
pwd=#{pwd},
</if>
</set>
where id=#{id}
</update>
如果username不为空就修改username,pwd不为空就修改pwd,两者都不为空就两个都修改,两者都为空,报错。
为了解决报错的问题可以使用<trim>
<update ...>
update user
<trim prefix="set" suffixOverrides=",">
<if test="username!=null and username!=' ' ">
username=#{username},
</if>
<if test="pwd!=null and pwd!=' '">
pwd=#{pwd},
</if>
</trim>
where id=#{id}
</update>
效果与<set>一样,只不过如果username和pwd都为空也不会报错。
trim的prefix表示要添加的前缀,这里可以填where、set。
suffixOverrides表示要去掉的后缀,这段代码中的','表示去掉最后一个逗号。
还有prefixOverrides表示要去掉的前缀,suffix表示要添加的后缀。
<choose>、<when>和<otherwise>
<choose>、<when>、<otherwise>类似于java中的switch、case和default。所以它的作用也是判断。和java中不同的是 ,choose会比较多个when。
用法:
<select ...>
select * from user
<trim prefix="where" suffixOverwrites="and">
<choose>
<when test="username!=null and username!=' '">
username=#{username} and
</when>
<when test="job!=null and job!=0">
job=#{job} and
</when>
<otherwise>
id={id}
</otherwise>
</choose>
</trim>
</select>
当username不为空,查找username,job不为空查找job,两者都不为空全都查找,两者都为空查找id。
复杂查询操作-<foreach>
在程序开发过程中需要实现“允许用户插入多条数据”或者“允许用户根据多个id查询用户”的功能。而正常的insert并不能完成这项工作。foreach就可以解决这个问题。
<foreach>操作数组和List
<select ...>
select * from user where id in
<foreach collection="ids" item="i" open="(" separator="," close=")">#{i}</foreach>
</select>
foreach的collection属性是参数中的数组或者List,item为数组或者List中的元素,open和end表示这些数组元素用什么包裹,代码中用的是()
包裹,separator表示多个#{i}
之间用什么分割。还有index属性(代码中未提及)表示当前元素在数组或List中的索引。
假如ids数组有元素[1,2,5]
那么正确的sql语句是select * from user where id in ('1','2','5')
下面这个例子是插入多条数据的例子:
<insert ...>
insert into user
<trim prefix="values" suffixOverrides=",">
<foreach collection="users" item="user" sparator=",">
(default,#{user.name},#{user.age})
</foreach>
</trim>
</insert>
这里面,sparator就变成了多个(default,#{user.name},#{user.age})
之间用什么分割。这样就实现插入多条数据了。
类似的还可以实现同时删除多条数据。这里就不再介绍了。
<foreach>操作map
和数组以及List相似,只不过,index中是map的key而item是对应key的值。而collection实际上是map中key的集合。比如:
<foreach collection="ids" item="val" index="key" sparator=";">#{key},#{val}
假设参数 ids(Map类型)中是这样的数据:[<1,"张三">,<2,"李四">] ,那么结果是1,'张三';2,'李四';