04、MyBatis DynamicSQL(Mybatis动态SQL)
1.动态SQL简介
动态 SQL是MyBatis强大特性之一.
动态 SQL 元素和使用 JSTL 或其他类似基于 XML 的文本处理器相似.
MyBatis 采用功能强大的基于 OGNL 的表达式来简化操作.
2.if
1).实现DynamicSQL
public interface EmployeeMapperDynamicSQL { public List<Employee> getEmpsByCondtionIf(Employee employee); }
<mapper namespace="com.atguigu.mybatis.dao.EmployeeMapperDynamicSQL"> <!-- 查询员工:携带指定字段查询条件就带上该字段的值 --> <!-- public List<Employee> getEmpsByCondtionIf(Employee employee) --> <select id="getEmpsByCondtionIf" resultType="com.atguigu.mybatis.bean.Employee"> select * from tbl_employee <!-- where:根据条件包含 where 子句 --> where <!-- test:判断表达式(OGNL) --> <!-- OGNL语法参照PPT或者官方文档:http://commons.apache.org/proper/commons-ognl/language-guide.html --> <!-- 从参数中取值进行判断,如果遇到特殊符号去写转义字符,查W3C HTML ISO-8859-1 参考手册 --> <if test="id!=null"> id=#{id} </if> <!-- <if test="lastName!=null and lastName!="""> --> <!-- <if test="lastName!=null && lastName!=''"> --> <if test="lastName!=null && lastName!="""> and last_name like #{lastName} </if> <if test="email!=null and email.trim()!="""> and email=#{email} </if> <!-- ognl会进行字符串与数字的转换判断 "0"==0 --> <if test="gender==0 or gender==1"> and gender=#{gender} </if> </select> </mapper>
// select * from tbl_employee WHERE id=? and last_name like ? and email=? // Employee employee = new Employee(3, "%e%","atguigu@atguigu.com", null); //select * from tbl_employee WHERE id=? and last_name like ? Employee employee = new Employee(null, "%e%",null, null); List<Employee> emps = mapper.getEmpsByCondtionIf(employee); for(Employee emp:emps) { System.out.println(emp); }
2.choose
有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用.针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句.
public List<Employee> getEmpByConditionChoose(Employee employee);
<mapper namespace="com.atguigu.mybatis.dao.EmployeeMapperDynamicSQL"> <!-- public List<Employee> getEmpByConditionChoose(Employee employee) --> <select id="getEmpByConditionChoose" resultType="com.atguigu.mybatis.bean.Employee"> select * from tbl_employee <where> <!-- 如果带了id就用id查,如果带了lastName就用lastName查;只会进入其中一个 --> <choose> <when test="id!=null"> id=#{id} </when> <when test="lastName!=null"> last_name like #{lastName} </when> <when test="email!=null"> email = #{email} </when> <otherwise> gender = 0 </otherwise> </choose> </where> </select> </mapper>
//测试choose Employee employee = new Employee(3, "%e%",null, null); List<Employee> list = mapper.getEmpByConditionChoose(employee); for(Employee emp:list) { System.out.println(emp); }
3.trim
1).where
public List<Employee> getEmpsByCondtionTrim(Employee employee);
<mapper namespace="com.atguigu.mybatis.dao.EmployeeMapperDynamicSQL"> <!-- public List<Employee> getEmpsByCondtionTrim(Employee employee) --> <select id="getEmpsByCondtionTrim" resultType="com.atguigu.mybatis.bean.Employee"> select * from tbl_employee <!-- 后面多出的and或者or where标签不能解决,我们使用trim定制where元素功能 --> <!-- 自定义字符串的截取规则 --> <!-- trim:根据条件包含 where 子句 --> <!-- trim标签体中是整个字符串拼串 后的结果 --> <!-- 通过自定义 trim 元素来定制 where 元素的功能 --> <!-- prefix:前缀;prefix给拼串后的整个字符串加一个前缀 如:where <===> <trim prefix="where"> --> <!-- prefixOverrides:前缀覆盖;去掉整个字符串前面多余的字符 --> <!-- suffix:后缀;suffix给拼串后的整个字符串加一个后缀 --> <!-- suffixOverrides:后缀覆盖;去掉整个字符串后面多余的字符 --> <trim prefix="where" suffixOverrides="and"> <if test="id!=null"> id=#{id} and </if> <if test="lastName!=null && lastName!="""> last_name like #{lastName} and </if> <if test="email!=null and email.trim()!="""> email=#{email} and </if> <!-- ognl会进行字符串与数字的转换判断 "0"==0 --> <if test="gender==0 or gender==1"> gender=#{gender} </if> </trim> </select> </mapper>
@Test public void testDynamicSql() throws IOException { SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(); SqlSession openSession = sqlSessionFactory.openSession(); try { EmployeeMapperDynamicSQL mapper = openSession.getMapper(EmployeeMapperDynamicSQL.class); // select * from tbl_employee WHERE id=? and last_name like ? and email=? // Employee employee = new Employee(3, "%e%","atguigu@atguigu.com", null); //select * from tbl_employee WHERE id=? and last_name like ? Employee employee = new Employee(3, "%e%",null, null); List<Employee> emps = mapper.getEmpsByCondtionIf(employee); for(Employee emp:emps) { System.out.println(emp); } //查询的时候如果某些条件没带可能sql拼装会有问题 //1、给where后面加上1=1,以后的条件都and xxx. //2、mybatis使用where标签来将所有的查询条件包括在内。 //mybatis就会将where标签中拼装的sql,多出来的and或者or去掉 //where只会去掉第一个多出来的and或者or。 //测试Trim List<Employee> emps2 = mapper.getEmpsByCondtionTrim(employee); for(Employee emp:emps2) { System.out.println(emp); } } finally { // : handle finally clause openSession.close(); } }
2).set
①.使用set更新
public void updateEmp(Employee employee);
<mapper namespace="com.atguigu.mybatis.dao.EmployeeMapperDynamicSQL"> <update id="updateEmp"> <!-- set:更新拼串 --> <!-- set标签的使用 --> update tbl_employee <set> <if test="lastName!=null"> last_name=#{lastName}, </if> <if test="email!=null"> email=#{email}, </if> <if test="gender!=null"> gender=#{gender} </if> </set> where id=#{id} </update> </mapper>
//调试set标签 Employee employee = new Employee(1, "Adminn",null, null); mapper.updateEmp(employee); openSession.commit();
②.使用trim拼串更新
public void updateEmp(Employee employee);
<mapper namespace="com.atguigu.mybatis.dao.EmployeeMapperDynamicSQL"> <!-- public void updateEmp(Employee employee) --> <update id="updateEmp"> <!-- Trim:更新拼串 --> update tbl_employee <trim prefix="set" suffixOverrides=","> <if test="lastName!=null"> last_name=#{lastName}, </if> <if test="email!=null"> email=#{email}, </if> <if test="gender!=null"> gender=#{gender} </if> </trim> where id=#{id} </update> </mapper>
//调试set标签 Employee employee = new Employee(1, "Adminn",null, null); mapper.updateEmp(employee); openSession.commit();
4.foreach
动态 SQL 的另外一个常用的必要操作是需要对一个集合进行遍历,通常是在构建 IN 条件语句的时候.
当迭代列表、集合等可迭代对象或者数组时;index是当前迭代的次数,item的值是本次迭代获取的元素.
当使用字典(或者Map.Entry对象的集合)时,index是键,item是值.
1).MySQL
(1).遍历记录
//查询员工id'在给定集合中的 public List<Employee> getEmpsByConditionForeach(@Param("ids")List<Integer> ids);
<mapper namespace="com.atguigu.mybatis.dao.EmployeeMapperDynamicSQL"> <!-- public List<Employee> getEmpsByConditionForeach(List<Integer> ids) --> <select id="getEmpsByConditionForeach" resultType="com.atguigu.mybatis.bean.Employee"> <!-- collection:指定要遍历的集合;list类型的参数会特殊处理封装在map中,map的key就是list --> <!-- item:当前遍历出的元素赋值给指定的变量 --> <!-- separator:每个元素之间的分隔符 --> <!-- open:遍历出所有结果拼接一个开始的字符 --> <!-- close:遍历出所有结果拼接一个结束的字符 --> <!-- index:索引;遍历list的时候是index就是索引,item就是当前值;遍历map的时候index表示的就是map的key,item就是map的值 --> <!-- #{变量名}:能取出变量的值也就是当前遍历出的元素 --> select * from tbl_employee <foreach collection="ids" item="item_id" separator="," open="where id in(" close=")"> #{item_id} </foreach> </select> </mapper>
//测试foreach List<Employee> list = mapper.getEmpsByConditionForeach(Arrays.asList(1,2,3,4)); for(Employee emp : list) { System.out.println(emp); }
(2).批量保存记录1
public void addEmps(@Param("emps")List<Employee> emps);
<mapper namespace="com.atguigu.mybatis.dao.EmployeeMapperDynamicSQL"> <!-- 批量保存数据 --> <!-- MySQL下批量保存:可以foreach遍历 mysql支持values(),(),()语法 --> <!-- public void addEmps(@Param("emps")List<Employee> emps) --> <!-- insert 方式一 --> <!-- 推荐使用inert 方式一 --> <insert id="addEmps"> insert into tbl_employee(last_name,email,gender,d_id) values <foreach collection="emps" item="emp" separator=","> (#{emp.lastName},#{emp.email},#{emp.gender},#{emp.dept.id}) </foreach> </insert> </mapper>
List<Employee> emps = new ArrayList<>(); emps.add(new Employee(null, "smith", "smith@atguigu.com", "1",new Department(1))); emps.add(new Employee(null, "allen", "allen@atguigu.com", "0",new Department(1))); mapper.addEmps(emps); openSession.commit();
(3).批量保存记录2
public void addEmps(@Param("emps")List<Employee> emps);
<mapper namespace="com.atguigu.mybatis.dao.EmployeeMapperDynamicSQL"> <!-- insert 方式二 --> <!-- public void addEmps(@Param("emps")List<Employee> emps) --> <!-- 这种方式需要数据库连接属性allowMultiQueries=true; 这种分号分隔多个sql可以用于其他的批量操作(删除,修改) --> <insert id="addEmps"> <foreach collection="emps" item="emp" separator=";"> insert into tbl_employee(last_name,email,gender,d_id) values(#{emp.lastName},#{emp.email},#{emp.gender},#{emp.dept.id}) </foreach> </insert> </mapper>
List<Employee> emps = new ArrayList<>(); emps.add(new Employee(null, "smith", "smith@atguigu.com", "1",new Department(1))); emps.add(new Employee(null, "allen", "allen@atguigu.com", "0",new Department(1))); mapper.addEmps(emps); openSession.commit();
2).Oracle
(1).批量保存1
<mapper namespace="com.atguigu.mybatis.dao.EmployeeMapperDynamicSQL"> <insert id="addEmps" databaseId="oracle"> <!-- oracle第一种批量方式 --> <!-- <foreach collection="emps" item="emp" open="begin" close="end;"> insert into employees(employee_id,last_name,email) values(employees_seq.nextval,#{emp.lastName},#{emp.email}); </foreach> </insert> </mapper>
(2).批量保存2
<mapper namespace="com.atguigu.mybatis.dao.EmployeeMapperDynamicSQL"> <insert id="addEmps" databaseId="oracle"> <!-- oracle第二种批量方式 --> insert into employees( <!-- 引用外部定义的sql --> <include refid="insertColumn"> <property name="testColomn" value="abc"/> </include> ) <foreach collection="emps" item="emp" separator="union" open="select employees_seq.nextval,lastName,email from(" close=")"> select #{emp.lastName} lastName,#{emp.email} email from dual </foreach> </insert> </mapper>
5.bind
bind 元素可以从 OGNL 表达式中创建一个变量并将其绑定到上下文.
1).bind
若在 mybatis 配置文件中配置了 databaseIdProvider , 则可以使用 “_databaseId”变量,这样就可以根据不同的数据库厂商构建特定的语句.
public List<Employee> getEmpsTestInnerParameter(Employee employee);
<mapper namespace="com.atguigu.mybatis.dao.EmployeeMapperDynamicSQL"> <!-- public List<Employee> getEmpsTestInnerParameter(Employee employee) --> <!-- mybatis默认还有两个内置参数:_parameter|_databaseId --> <!-- _parameter:代表整个参数;单个参数:_parameter就是这个参数;多个参数:参数会被封装为一个map,_parameter就是代表这个map --> <!-- _databaseId:如果配置了databaseIdProvider标签;_databaseId就是代表当前数据库的别名 --> <select id="getEmpsTestInnerParameter" resultType="com.atguigu.mybatis.bean.Employee"> <!-- bind:可以将OGNL表达式的值绑定到一个变量中,方便后来引用这个变量的值 --> <if test="_databaseId=='mysql'"> select * from tbl_employee <if test="_parameter!=null"> where last_name like #{lastName} </if> </if> <if test="_databaseId=='oracle'"> select * from employees <if test="_parameter!=null"> where last_name like #{_parameter.lastName} </if> </if> </select> </mapper>
@Test public void testInnerParam() throws IOException{ SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(); SqlSession openSession = sqlSessionFactory.openSession(); try{ EmployeeMapperDynamicSQL mapper = openSession.getMapper(EmployeeMapperDynamicSQL.class); Employee employee2 = new Employee(); employee2.setLastName("%e%"); List<Employee> list = mapper.getEmpsTestInnerParameter(employee2); for (Employee employee : list) { System.out.println(employee); } }finally{ openSession.close(); } }
2).SQL片段
public void addEmps(@Param("emps")List<Employee> emps);
<mapper namespace="com.atguigu.mybatis.dao.EmployeeMapperDynamicSQL"> <!-- insert 方式三 --> <insert id="addEmps"> insert into tbl_employee( <include refid="insertColumn"></include> ) values <foreach collection="emps" item="emp" separator=","> (#{emp.lastName},#{emp.email},#{emp.gender},#{emp.dept.id}) </foreach> </insert> <!-- 抽取可重用的sql片段;方便后面引用 --> <!-- 1、sql抽取:经常将要查询的列名,或者插入用的列名抽取出来方便引用 --> <!-- 2、include来引用已经抽取的sql --> <!-- 3、include还可以自定义一些property,sql标签内部就能使用自定义的属性 --> <!-- include-property:取值的正确方式${prop} #{不能使用这种方式} --> <sql id="insertColumn"> <if test="_databaseId=='oracle'"> employee_id,last_name,email </if> <if test="_databaseId=='mysql'"> last_name,email,gender,d_id </if> </sql> </mapper>
@Test public void testBatchSave() throws IOException { SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(); SqlSession openSession = sqlSessionFactory.openSession(); try { EmployeeMapperDynamicSQL mapper = openSession.getMapper(EmployeeMapperDynamicSQL.class); List<Employee> emps = new ArrayList<>(); emps.add(new Employee(null, "smith", "smith@atguigu.com", "1",new Department(1))); emps.add(new Employee(null, "allen", "allen@atguigu.com", "0",new Department(1))); mapper.addEmps(emps); openSession.commit(); }finally { openSession.close(); } }
6.OGNL
参考文档:http://commons.apache.org/proper/commons-ognl/language-guide.html