MyBatis动态SQL
在实际项目中经常会遇到多条件查询的情况,比如淘宝可以根据不同的条件筛选商品。其中经常出现很多条件不取值的情况,那么这种情况在后台应该如何完成最终的SQL语句呢?
如果采用JDBC进行处理,需要根据条件是否取值进行SQL语句的拼接,一般情况下是使用StringBuilder类及其append方法实现,还是有些繁琐的。如果你有使用 JDBC 或其它类似框架的经验,你就能体会到根据不同条件拼接 SQL语句的痛苦。例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。
- MyBatis在简化操作方法提出了动态SQL功能,将使用Java代码拼接SQL语句,改变为在XML映射文件中截止标签拼接SQL语句。相比而言,大大减少了代码量,更灵活、高度可配置、利于后期维护。
- MyBatis中动态SQL是编写在mapper.xml中的,其语法和JSTL类似,但是却是基于强大的OGNL表达式实现的。
- MyBatis也可以在注解中配置SQL,但是由于注解功能受限,尤其是对于复杂的SQL语句,可读性很差,所以较少使用。
一、If标签
1.1.接口准备
在mapper包下创建EmpMapper2,在里面创建接口如下:
public interface EmpMapper2 { /* * 动态SQL:查询员工信息 * */ List<Emp> findByCondittion(Emp emp); }
1.2.创建映射文件
在mapper包下创建EmpMapper2.xml文件内容如下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- namespace需要指定接口文件的路径 --> <mapper namespace="com.augus.mapper.EmpMapper2"> <select id="findByCondittion" parameterType="emp" resultType="emp"> /* 这里where后面需要添加1=1.否则在动态SQL后续如果没有任何参数,则会出现错误 */ SELECT * FROM emp where 1=1 <if test="empno != null"> and empno = #{empno} </if> <if test="ename != null and ename != ''"> and ename like concat("%",#{ename},"%") </if> <if test="job != null and job != ''"> and job = #{job} </if> <if test="mgr != null and job != ''"> and job = #{job} </if> <if test="hiredate != null and hiredate != ''"> and hiredate = #{hiredate} </if> <if test="sal != null and sal != ''"> and sal = #{sal} </if> <if test="comm != null and comm != ''"> and comm = #{comm} </if> <if test="deptno != null and deptno != ''"> and deptno = #{deptno} </if> </select> </mapper>
1.3.添加配置文件
在sqlMapConfig.xml配置,添加刚刚创建的 EmpMapper2.xml,如下:

1.4.创建测试代码
创建测试类Test3内容如下:
public class Test03 { private SqlSession sqlSession; @BeforeEach public void setUp(){ SqlSessionFactoryBuilder ssfb =new SqlSessionFactoryBuilder(); InputStream resourceAsStream = null; try { resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml"); } catch (IOException e) { e.printStackTrace(); } SqlSessionFactory factory=ssfb.build(resourceAsStream) ; sqlSession=factory.openSession(); } @Test public void testFindByCondittion(){ //获取mapper EmpMapper2 mapper = sqlSession.getMapper(EmpMapper2.class); //创建emp对象 Emp emp = new Emp(); emp.setSal(1250.00); emp.setDeptno(30); //执行 List<Emp> emps = mapper.findByCondittion(emp); for (Emp emp1 : emps) { System.out.println(emp1); } } @AfterEach public void tearDown(){ //关闭 sqlSession.close(); } }
二、Where标签
上面在写条件的时候,在SQL中直接加了where,那么在mybatis中提供了where标签,用于简化上面的操作,使用where标签在SQL中就不需要写where关键字,其次之前为了形式多个条件连接构建格式 1=1 也就不需要写了,where标签会自动处理
1.接口准备
package com.augus01.mapper; import com.augus01.pojo.Emp; import java.util.List; public interface EmpMapper2 { List<Emp> findByCondittion(Emp emp); }
2.映射文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- namespace需要指定接口文件的路径 --> <mapper namespace="com.augus01.mapper.EmpMapper2"> <!-- List<Emp> findByCondittion(Emp emp); --> <select id="findByCondittion" resultType="emp"> select * from emp <where> <if test="empno != null" > and empno=#{empno} </if> <if test="ename != null and ename != ''"> and ename like concat('%',#{ename},'%') </if> <if test="job != null and job != ''"> and job = #{job} </if> <if test="mgr != null"> and mgr = #{mgr} </if> <if test="hiredate != null"> and hiredate = #{hiredate} </if> <if test="sal != null"> and sal = #{sal} </if> <if test="comm != null"> and comm = #{comm} </if> <if test="deptno != null"> and deptno = #{deptno } </if> </where> </select> </mapper>
3.测试文件
import com.augus01.mapper.EmpMapper; import com.augus01.mapper.EmpMapper2; import com.augus01.pojo.Emp; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Before; import org.junit.Test; import java.io.IOException; import java.io.InputStream; import java.util.Date; import java.util.List; public class Test7 { private SqlSession sqlSession; @Before public void init(){ SqlSessionFactoryBuilder ssfb =new SqlSessionFactoryBuilder(); InputStream resourceAsStream = null; try { resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml"); } catch (IOException e) { e.printStackTrace(); } SqlSessionFactory factory=ssfb.build(resourceAsStream) ; sqlSession=factory.openSession(); } //测试动态SQL @Test public void testfindByCondittion(){ EmpMapper2 mapper = sqlSession.getMapper(EmpMapper2.class); //创建emp实例 Emp emp = new Emp(); //设置属性 emp.setDeptno(30); /*emp.setSal(2850.0);*/ //执行SQL List<Emp> byCondittion = mapper.findByCondittion(emp); for (Emp emp1 : byCondittion) { System.out.println(emp1); } } }
三、Choose标签
有时候我们并不想应用所有的条件,而只是想从多个选项中选择一个。而使用if标签时,只要test中的表达式为 true,就会执行 if 标签中的条件。MyBatis 提供了 choose 元素。if标签是与(and)的关系,而 choose 是或(or)的关系。
choose标签是按顺序判断其内部when标签中的test条件出否成立(前面的when条件成立 后面的 when就不再判断了),如果有一个成立,则 choose 结束。当 choose 中所有 when 的条件都不满则时,则执行 otherwise 中的sql。类似于Java 的 switch 语句,choose 为 switch,when 为 case,otherwise 则为 default。
1.接口
package com.augus01.mapper; import com.augus01.pojo.Emp; import java.util.List; public interface EmpMapper2 { List<Emp> findByCondittion2(Emp emp); }
2.映射文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- namespace需要指定接口文件的路径 --> <mapper namespace="com.augus01.mapper.EmpMapper2"> <!--findByCondittion2--> <select id="findByCondittion2" resultType="emp"> select * from emp <where> <choose> <when test="empno != null" > and empno=#{empno} </when> <when test="ename != null and ename != ''"> and ename like concat('%',#{ename},'%') </when> <when test="job != null and job != ''"> and job = #{job} </when> <when test="mgr != null"> and mgr = #{mgr} </when> <when test="hiredate != null"> and hiredate = #{hiredate} </when> <when test="sal != null"> and sal = #{sal} </when> <when test="comm != null"> and comm = #{comm} </when> <when test="deptno != null"> and deptno = #{deptno} </when> </choose> </where> </select> </mapper>
3.测试代码
import com.augus01.mapper.EmpMapper2; import com.augus01.pojo.Emp; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Before; import org.junit.Test; import java.io.IOException; import java.io.InputStream; import java.util.Date; import java.util.List; public class Test7 { private SqlSession sqlSession; @Before public void init(){ SqlSessionFactoryBuilder ssfb =new SqlSessionFactoryBuilder(); InputStream resourceAsStream = null; try { resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml"); } catch (IOException e) { e.printStackTrace(); } SqlSessionFactory factory=ssfb.build(resourceAsStream) ; sqlSession=factory.openSession(); }//动态SQL choose when 标签 @Test public void testfindByCondittion2(){ EmpMapper2 mapper = sqlSession.getMapper(EmpMapper2.class); //产生Emp实例 Emp emp = new Emp(); emp.setDeptno(30); emp.setSal(40000.0); //执行SQL List<Emp> byCondittion2 = mapper.findByCondittion2(emp); for (Emp emp1 : byCondittion2) { System.out.println(emp1); } } }
四、Set标签
MyBatis在生成update语句时若使用if标签,如果前面的if没有执行,则可能导致有多余逗号的错误。使用set标签可以将动态的配置SET 关键字,和剔除追加到条件末尾的任何不相关的逗号(所以一般set和if配合使用,)。 没有使用if标签时,如果有一个参数为null,都会导致错误
1.接口
package com.augus01.mapper; import com.augus01.pojo.Emp; import java.util.List; public interface EmpMapper2 { int updateEmpByCondtion(Emp emp); }
2.映射文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- namespace需要指定接口文件的路径 --> <mapper namespace="com.augus01.mapper.EmpMapper2"> <!--int updateEmpByCondtion(Emp emp); 根据empno修改员工信息,下面修改的条件中不包含empno --> <update id="updateEmpByCondtion" parameterType="int"> update emp <set> <if test="ename != null and ename != ''"> , ename = #{ename} </if> <if test="job != null and job != ''"> , job = #{job} </if> <if test="mgr != null"> , mgr = #{mgr} </if> <if test="hiredate != null"> , hiredate = #{hiredate} </if> <if test="sal != null"> , sal = #{sal} </if> <if test="comm != null"> , comm = #{comm} </if> <if test="deptno != null"> , deptno = #{deptno} </if> </set> <where> empno=#{empno} </where> </update> </mapper>
3.测试代码
import com.augus01.mapper.EmpMapper; import com.augus01.mapper.EmpMapper2; import com.augus01.pojo.Emp; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Before; import org.junit.Test; import java.io.IOException; import java.io.InputStream; import java.util.Date; import java.util.List; public class Test7 { private SqlSession sqlSession; @Before public void init(){ SqlSessionFactoryBuilder ssfb =new SqlSessionFactoryBuilder(); InputStream resourceAsStream = null; try { resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml"); } catch (IOException e) { e.printStackTrace(); } SqlSessionFactory factory=ssfb.build(resourceAsStream) ; sqlSession=factory.openSession(); } //set 标签 @Test public void testupdateEmpByCondtion(){ EmpMapper2 mapper = sqlSession.getMapper(EmpMapper2.class); Emp emp = new Emp(); //设置参数 emp.setEmpno(7934); emp.setEname("无忌"); //执行SQL int i = mapper.updateEmpByCondtion(emp); //提交事务 sqlSession.commit(); System.out.println(i); } }
五、Trim标签
MyBatis的trim标签一般用于去除sql语句中多余的and关键字,逗号,或者给sql语句前拼接 “where“、“set“以及“values(“ 等前缀,或者添加“)“等后缀,可用于选择性插入、更新、删除或者条件查询等操作,trim标签的功能简单来说就是自定义格式拼凑SQL语句。
trim有4个属性:
- prefix:表示在trim包裹的SQL前添加指定内容
- suffix:表示在trim包裹的SQL末尾添加指定内容
- prefixOverrides:表示去掉(覆盖)trim包裹的SQL的指定首部内容
- suffixOverrides:表示去掉(覆盖)trim包裹的SQL的指定尾部内容
下面利用Trim 标签处理set
1.接口
package com.augus01.mapper; import com.augus01.pojo.Emp; import java.util.List; public interface EmpMapper2 { int updateEmpByCondtion(Emp emp); }
2.映射文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- namespace需要指定接口文件的路径 --> <mapper namespace="com.augus01.mapper.EmpMapper2"> <!--int updateEmpByCondtion(Emp emp); 根据empno修改员工信息,下面修改的条件中不包含empno --> <update id="updateEmpByCondtion" parameterType="int"> update emp <!-- 这里加上去前缀set,就类似于加上了set标签,suffixOverrides="," 去掉后缀逗号 如果按照下面写法 SQL 就会是 update emp set ename="张三", where empno=7934 张三后面会多一个逗号,这样会报错,所以利用suffixOverrides去掉逗号 --> <trim prefix="set" suffixOverrides=","> <if test="ename != null and ename != ''"> ename = #{ename}, </if> <if test="job != null and job != ''"> job = #{job}, </if> <if test="mgr != null"> mgr = #{mgr}, </if> <if test="hiredate != null"> hiredate = #{hiredate}, </if> <if test="sal != null"> sal = #{sal}, </if> <if test="comm != null"> comm = #{comm}, </if> <if test="deptno != null"> deptno = #{deptno} </if> </trim> <where> empno=#{empno} </where> </update> </mapper>
3.测试代码
import com.augus01.mapper.EmpMapper; import com.augus01.mapper.EmpMapper2; import com.augus01.pojo.Emp; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Before; import org.junit.Test; import java.io.IOException; import java.io.InputStream; import java.util.Date; import java.util.List; public class Test7 { private SqlSession sqlSession; @Before public void init(){ SqlSessionFactoryBuilder ssfb =new SqlSessionFactoryBuilder(); InputStream resourceAsStream = null; try { resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml"); } catch (IOException e) { e.printStackTrace(); } SqlSessionFactory factory=ssfb.build(resourceAsStream) ; sqlSession=factory.openSession(); }//set 标签 @Test public void testupdateEmpByCondtion(){ EmpMapper2 mapper = sqlSession.getMapper(EmpMapper2.class); Emp emp = new Emp(); //设置参数 emp.setEmpno(7934); emp.setEname("奥琦12"); //执行SQL int i = mapper.updateEmpByCondtion(emp); //提交事务 sqlSession.commit(); System.out.println(i); } }
六、Bind标签
Bind标签一般用于处理模糊查询的模板
1.接口
package com.augus01.mapper; import com.augus01.pojo.Emp; import org.apache.ibatis.annotations.Param; import java.util.List; public interface EmpMapper2 {//根据员工姓名 进行模糊查询 List<Emp> findEmpByEname(@Param("name") String name); }
2.映射文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- namespace需要指定接口文件的路径 --> <mapper namespace="com.augus01.mapper.EmpMapper2"> <!-- //根据员工姓名 进行模糊查询 List<Emp> findEmpByEname(@Param("name") String name); --> <select id="findEmpByEname" resultType="emp"> <!-- 模糊查询 param1 取的传入的第一个参数 --> <bind name="likename" value="'%'+param1+'%'"/> select * from emp where ename like #{likename} </select> </mapper>
3.测试文件
import com.augus01.mapper.EmpMapper; import com.augus01.mapper.EmpMapper2; import com.augus01.pojo.Emp; import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Before; import org.junit.Test; import javax.xml.transform.Source; import java.io.IOException; import java.io.InputStream; import java.util.Date; import java.util.List; public class Test7 { private SqlSession sqlSession; @Before public void init(){ SqlSessionFactoryBuilder ssfb =new SqlSessionFactoryBuilder(); InputStream resourceAsStream = null; try { resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml"); } catch (IOException e) { e.printStackTrace(); } SqlSessionFactory factory=ssfb.build(resourceAsStream) ; sqlSession=factory.openSession(); } // @Test public void testfindEmpByEname(){ EmpMapper2 mapper = sqlSession.getMapper(EmpMapper2.class); //执行SQL List<Emp> a = mapper.findEmpByEname("A"); System.out.println(a); } }
七、Sql标签
SQL标签用于定义sql片段,方便在其他SQL标签里面复用,在其他地方复用的时候需要使用<include></include>子标签,<sql>可以定义sql的任何部分,所以<include>标签可以放在动态SQL的任何位置。
1.接口
package com.augus01.mapper; import com.augus01.pojo.Emp; import org.apache.ibatis.annotations.Param; import java.util.List; public interface EmpMapper2 { //根据员工姓名 进行模糊查询 List<Emp> findEmpByEname(@Param("name") String name); }
2.映射文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- namespace需要指定接口文件的路径 --> <mapper namespace="com.augus01.mapper.EmpMapper2"> <!-- 定义SQL文件方便后面复用 --> <sql id="empCol">empno,ename,job,mgr,hiredate,sal,comm,deptno</sql> <sql id="baseSelect">select <include refid="empCol"></include> from emp </sql> <!-- //根据员工姓名 进行模糊查询 List<Emp> findEmpByEname(@Param("name") String name); --> <select id="findEmpByEname" resultType="emp"> <!-- 模糊查询 param1 取的传入的第一个参数 --> <bind name="likename" value="'%'+param1+'%'"/> <!-- 应用上面拼接的SQL片段 --> <include refid="baseSelect"></include> where ename like #{likename} </select> </mapper>
3.测试文件
import com.augus01.mapper.EmpMapper2; import com.augus01.pojo.Emp; import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Before; import org.junit.Test; import javax.xml.transform.Source; import java.io.IOException; import java.io.InputStream; import java.util.Date; import java.util.List; public class Test7 { private SqlSession sqlSession; @Before public void init(){ SqlSessionFactoryBuilder ssfb =new SqlSessionFactoryBuilder(); InputStream resourceAsStream = null; try { resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml"); } catch (IOException e) { e.printStackTrace(); } SqlSessionFactory factory=ssfb.build(resourceAsStream) ; sqlSession=factory.openSession(); } // @Test public void testfindEmpByEname(){ EmpMapper2 mapper = sqlSession.getMapper(EmpMapper2.class); //执行SQL List<Emp> a = mapper.findEmpByEname("A"); System.out.println(a); } }
八、Foreach标签
动态SQL要有一个比较多的操作是对一个集合进行遍历,通常是在构建IN条件语句的时候。需要注意的点:
- collection 表示需要遍历的集合类型,array表示需要遍历的数组
- open,close,separator是对遍历内容的SQL拼接
- foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及在迭代结果之间放置分隔符。
- 你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象传递给 foreach 作为集合参数。当使用可迭代对象或者数组时,index 是当前迭代的次数,item 的值是本次迭代获取的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。
例如:我们要查询empno为7839、7900、7499的员工信息
1.接口
package com.augus01.mapper; import com.augus01.pojo.Emp; import org.apache.ibatis.annotations.Param; import java.util.List; public interface EmpMapper2 {//我们要查询empno为7839、7900、7499的员工信息 List<Emp> findEmpByEmpnos(List<Integer> empnos); }
2.映射文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- namespace需要指定接口文件的路径 --> <mapper namespace="com.augus01.mapper.EmpMapper2"> <!--//我们要查询empno为7839、7900、7499的员工信息 List<Emp> findEmpByEmpno(List<Integer> empnos);--> <select id="findEmpByEmpnos" resultType="emp" parameterType="list"> select * from emp where empno in <!-- collection="" 遍历的集合或者是数组 参数是数组,collection中名字指定为array 参数是List集合,collection中名字指定为list separator="" 多个元素取出的时候 用什么文字分隔 open="" 以什么开头 close="" 以什么结尾 item="" 中间变量名 for(Person per:PersonList) --> <foreach collection="list" separator="," open="(" close=")" item="empno"> #{empno} </foreach> </select> </mapper>
3.测试文件
import com.augus01.mapper.EmpMapper2; import com.augus01.pojo.Emp; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Before; import org.junit.Test; import java.io.IOException; import java.io.InputStream; import java.util.*; public class Test7 { private SqlSession sqlSession; @Before public void init(){ SqlSessionFactoryBuilder ssfb =new SqlSessionFactoryBuilder(); InputStream resourceAsStream = null; try { resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml"); } catch (IOException e) { e.printStackTrace(); } SqlSessionFactory factory=ssfb.build(resourceAsStream) ; sqlSession=factory.openSession(); } @Test public void testfindEmpByEmpnos(){ EmpMapper2 mapper = sqlSession.getMapper(EmpMapper2.class); //创建list,给里面添加值,为int类型 ArrayList<Integer> empnos = new ArrayList<Integer>(); empnos.add(7839); empnos.add(7900); empnos.add(7900); //执行SQL List<Emp> empByEmpno = mapper.findEmpByEmpnos(empnos); System.out.println(empByEmpno); } }

浙公网安备 33010602011771号