三、动态SQL
经常遇到很多按照很多查询条件进行查询的情况,比如智联招聘的职位搜索,比如OA系统中的支出查询等。其中经常出现很多条件不取值的情况,在后台应该如何完成最终的SQL语句呢?
如果采用JDBC进行处理,需要根据条件是否取值进行SQL语句的拼接,一般情况下是使用StringBuilder类及其append方法实现,还是有些繁琐的。如果你有使用 JDBC 或其它类似框架的经验,你就能体会到根据不同条件拼接 SQL语句的痛苦。例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。
MyBatis在简化操作方法提出了动态SQL功能,将使用Java代码拼接SQL语句,改变为在XML映射文件中截止标签拼接SQL语句。相比而言,大大减少了代码量,更灵活、高度可配置、利于后期维护。
MyBatis中动态SQL是编写在mapper.xml中的,其语法和JSTL类似,但是却是基于强大的OGNL表达式实现的。
MyBatis也可以在注解中配置SQL,但是由于注解功能受限,尤其是对于复杂的SQL语句,可读性很差,所以较少使用。
定义如下接口及其功能来练习动态SQL语句。
public interface EmployeeMapper {
public List<Employee> findEmp(@Param("job") String job,
@Param("deptno") int deptno,
@Param("sal") double sal);
public List<Employee> findEmp2(@Param("job") String job,
@Param("deptno") int deptno,
@Param("sal") double sal);
public List<Employee> findEmp3(@Param("ename") String ename,
@Param("job") String job);
public List<Employee> findEmp4(@Param("ename") String ename,
@Param("job") String job);
public int updateEmp(String job,double sal,int empno);
public List<Employee> findEmp5(List<Integer> deptNoList);
public List<Employee>
findEmp6(@Param("deptnoList") List<Integer> deptNoList);
public List<Employee> findEmp7(int [] arr);
}
3.1 if
每一个if相当于一个if单分支语句。一般添加一个where 1=1 的查询所有数据的条件,作为第一个条件。这样可以让后面每个if语句的SQL语句都以and开始。
<mapper namespace="com.bjsxt.mapper.EmployeeMapper">
<select id="findEmp" resultType="employee">
select * from emp where 1=1
<if test="job!=null and job !='' ">
and job =#{job}
</if>
<if test="deptno !=0 ">
and deptno = #{deptno}
</if>
<if test="sal>0">
and sal > #{sal}
</if>
</select>
</mapper>
3.2 where
使用where元素,就不需要提供where 1=1 这样的条件了。如果标签内容不为空字符串则自动添加where关键字,并且会自动去掉第一个条件前面的and或or。
<mapper namespace="com.bjsxt.mapper.EmployeeMapper">
<select id="findEmp2" resultType="employee">
select * from emp
<where>
<if test="job!=null and job !='' ">
and job =#{job}
</if>
<if test="deptno >0 ">
and deptno = #{deptno}
</if>
<if test="sal>0">
and sal > #{sal}
</if>
</where>
</select>
</mapper>
3.3 bind
bind主要的一个重要场合是模糊查询,通过bind通配符和查询值,可以避免使用数据库的具体语法来进行拼接。比如MySQL中通过concat来进行拼接,而Oracle中使用||来进行拼接。
<mapper namespace="com.bjsxt.mapper.EmployeeMapper">
<select id="findEmp3" resultType="employee">
select * from emp where ename like concat("%",#{ename},"%" )
and job like concat("%",#{job},"%" )
</select>
<select id="findEmp4" resultType="employee">
<bind name="cename" value="'%'+ename+'%'"></bind>
<bind name="cjob" value="'%'+job+'%'"></bind>
select * from emp where ename like #{cename} and job like #{cjob}
</select>
</mapper>
3.4 set
set元素用在update语句中给字段赋值。借助if的配置,可以只对有具体值的字段进行更新。set元素会自动帮助添加set关键字,自动去掉最后一个if语句的多余的逗号。
<mapper namespace="com.bjsxt.mapper.EmployeeMapper">
<update id="updateEmp">
update emp
<set>
<if test="param1!=null and param1 != ''">
job = #{param1},
</if>
<if test="param2>0">
sal = #{param2}
</if>
</set>
where empno = #{param3}
</update>
</mapper>
3.5 foreach
foreach 元素是非常强大的,它允许你指定一个集合或者数组,声明集合项和索引变量,它们可以用在元素体内。它也允许你指定开放和关闭的字符串,在迭代之间放置分隔符。这个元素是很智能的,它不会偶然地附加多余的分隔符。
注意 你可以传递一个 List 实例或者数组作为参数对象传给 MyBatis。当你这么做的时候,MyBatis 会自动将它包装在一个 Map 中,用名称在作为键。List 实例将会以“list” 作为键,而数组实例将会以“array”作为键。
<mapper namespace="com.bjsxt.mapper.EmployeeMapper">
<select id="findEmp5" resultType="employee">
select * from emp where deptno in
<foreach collection="collection" item="deptno"
open="(" separator="," close=")">
#{deptno}
</foreach>
</select>
<select id="findEmp6" resultType="employee">
select * from emp where deptno in
<foreach collection="deptnoList" item="deptno"
open="(" separator="," close=")">
#{deptno}
</foreach>
</select>
<select id="findEmp7" resultType="employee">
select * from emp where deptno in
<foreach collection="array" item="deptno"
open="(" separator="," close=")">
#{deptno}
</foreach>
</select>
</mapper>
在进行SQL优化是有一点就是建议少使用in语句,因为对性能有影响。如果in中元素很多的话,会对性能有较大影响,此时就不建议使用foreach语句了。
作业
1. 动态SQL的优势
2. 练习动态SQL语句