四、MyBatis动态语句
1、动态语句需求和简介
1、需求
1、应对多个条件查询。
2、有很多条件不满足的。
2、简介
1、动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。
2、使用动态 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。
3、如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。
2、if和where标签
1、if标签
1、就是用来进行判断,哪些条件需要加
2、内容
- if 判断传入的参数,最终是否添加语句
- test属性: 内部做比较运算,最终true将标签内的sql语句进行拼接。如果为false就不拼接
- 判断语句:“key 比较符号 值 and | or key 比较符号 值 ”
- 大于小于不推荐直接使用符号。一般用实体符号来表示 大于:> 小于: <
2、where条件
1、就是用来解决,if判断中可能出现的问题
2、问题
- 如果两个条件都满足
- 如果第一个条件满足 emp_name = #{name}
- 如果第一个条件不满足,第二个条件满足
- 如果两个都不满足
3、where的作用
- 自动添加where关键字 where内部有任何一个if满足,就自动添加where关键字,否则去掉
- 自动去掉多余的and 和or
3、例子
1、准备实体类
package com.atgui.pojo;
public class Employee {
private int empId;
private String empName;
private String empSalary;
public int getEmpId() {
return empId;
}
public void setEmpId(int empId) {
this.empId = empId;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public String getEmpSalary() {
return empSalary;
}
public void setEmpSalary(String empSalary) {
this.empSalary = empSalary;
}
@Override
public String toString() {
return "Employee{" +
"empId=" + empId +
", empName='" + empName + '\'' +
", empSalary='" + empSalary + '\'' +
'}';
}
}
2、准备接口
package com.atgui.mapper;
import com.atgui.pojo.Employee;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface EmployeeMapper {
List<Employee> query(@Param("name") String name,@Param("salary") Double salary);
}
3、准备employee.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace等于mapper接口类的全限定名,这样实现对应 -->
<mapper namespace="com.atgui.mapper.EmployeeMapper">
<!--
场景:如果传入属性,就判断相等,如果不传入,就不加对应的条件
where
if 判断传入的参数,最终是否添加语句
test属性: 内部做比较运算,最终true将标签内的sql语句进行拼接。如果为false就不拼接
判断语句:“key 比较符号 值 and | or key 比较符号 值 ”
大于小于不推荐直接使用符号。一般用实体符号来表示 大于:> 小于: <
还有遇到的问题
如果两个条件都满足
如果第一个条件满足 emp_name = #{name}
如果第一个条件不满足,第二个条件满足
如果两个都不满足
解决:这里我们要用 where标签
where好处:
1、自动添加where关键字 where内部有任何一个if满足,就自动添加where关键字,否则去掉
2、自动去掉多余的and 和or
-->
<select id="query" resultType="employee">
select * from t_emp
<where>
<if test="name!=null">
emp_name = #{name}
</if>
<if test="salary !=null and salary >100 ">
and emp_salary =#{salary}
</if>
</where>
</select>
</mapper>
4、准备mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 让mybatis开启日志输出-->
<settings>
<!-- 这里设置的是选择用system进行日志输出-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!-- 开启驼峰式自动映射-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!--开启resultMap自动映射 -->
<setting name="autoMappingBehavior" value="FULL"/>
</settings>
<!-- 定义自己类的别名-->
<typeAliases>
<!-- 单独定义-->
<!-- <typeAlias type="com.atgui.pojo.Employee" alias="suibian"/>-->
<!-- 批量定义-->
<package name="com.atgui.pojo"/>
</typeAliases>
<!-- environments表示配置Mybatis的开发环境,可以配置多个环境,在众多具体环境中,使用default属性指定实际运行时使用的环境。default属性的取值是environment标签的id属性的值。 -->
<environments default="development">
<!-- environment表示配置Mybatis的一个具体的环境 -->
<environment id="development">
<!-- Mybatis的内置的事务管理器 -->
<transactionManager type="JDBC"/>
<!-- 配置数据源 -->
<dataSource type="POOLED">
<!-- 建立数据库连接的具体信息 -->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis-example"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<!-- Mapper注册:指定Mybatis映射文件的具体位置 -->
<!-- mapper标签:配置一个具体的Mapper映射文件 -->
<!-- resource属性:指定Mapper映射文件的实际存储位置,这里需要使用一个以类路径根目录为基准的相对路径 -->
<!-- 对Maven工程的目录结构来说,resources目录下的内容会直接放入类路径,所以这里我们可以以resources目录为基准 -->
<mapper resource="mappers/EmployeeMapper.xml"/>
</mappers>
</configuration>
5、测试
package com.atgui;
import com.atgui.mapper.EmployeeMapper;
import com.atgui.pojo.Employee;
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.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class mybatisTest {
private SqlSession sqlSession;
// 每次测试方法执行前都会执行该代码
@BeforeEach
public void before() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
sqlSession = sqlSessionFactory.openSession(true);
}
//每次测试方法执行后都会执行该代码
@AfterEach
public void clean(){
sqlSession.close();
}
@Test
public void test1(){
EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
List<Employee> list = employeeMapper.query(null,100.0);
System.out.println("list = " + list);
}
}
3、set标签
1、作用
1、 使用set标签动态管理set子句,并且动态去掉两端多余的逗号
2、会自动添加set关键字
2、例子
1、代码
<update id="update">
update t_emp
<!-- set作用:
1、自动去掉多余的,
2、自动添加set关键字
-->
<set>
<if test="empName!=null">
emp_name = #{empName},
</if>
<if test="empSalary !=null">
emp_salary = #{empSalary},
</if>
</set>
</update>
4、trim标签(了解)
1、作用
1、目前的作用是来替代where语句
2、可以控制条件部分两端是否包含某些字符串
2、属性
- prefix属性:指定要动态添加的前缀
- suffix属性:指定要动态添加的后缀
- prefixOverrides属性:指定要动态去掉的前缀,使用“|”分隔有可能的多个值
- suffixOverrides属性:指定要动态去掉的后缀,使用“|”分隔有可能的多个值
3、例子
<!-- List<Employee> selectEmployeeByConditionByTrim(Employee employee) -->
<select id="selectEmployeeByConditionByTrim" resultType="com.atguigu.mybatis.entity.Employee">
select emp_id,emp_name,emp_age,emp_salary,emp_gender
from t_emp
<!-- prefix属性指定要动态添加的前缀 -->
<!-- suffix属性指定要动态添加的后缀 -->
<!-- prefixOverrides属性指定要动态去掉的前缀,使用“|”分隔有可能的多个值 -->
<!-- suffixOverrides属性指定要动态去掉的后缀,使用“|”分隔有可能的多个值 -->
<!-- 当前例子用where标签实现更简洁,但是trim标签更灵活,可以用在任何有需要的地方 -->
<trim prefix="where" suffixOverrides="and|or">
<if test="empName != null">
emp_name=#{empName} and
</if>
<if test="empSalary > 3000">
emp_salary>#{empSalary} and
</if>
<if test="empAge <= 20">
emp_age=#{empAge} or
</if>
<if test="empGender=='male'">
emp_gender=#{empGender}
</if>
</trim>
</select>
5、choose/when/otherwise标签
1、作用
1、在多分支条件上,仅执行一个
- 从上到下依次执行条件判断
- 遇到的第一个满足条件的分支会被采纳
- 被采纳分支后面的分支都将不被考虑
- 如果所有的when分支都不满足,那么就执行otherwise分支
2、例子
<select id="selectEmployeeByConditionByChoose" resultType="employee">
select emp_id ,emp_name,emp_salary
from t_emp
where
<choose>
<when test="enpName != null">
emp_name =#{empName}
</when>
</choose>
</select>
6、foreach标签
1、基本用法
1、属性
- 1、collection:要遍历的集合
- 2、open:遍历之前追加的字符串
- 3、close:遍历结束需要添加的字符串
- 4、separator:每次遍历的分割符号,如果是最后一次,不会追加
- 5、item:获取每个遍历项
2、就是找规律,看sql语句哪些能够用foreach套用然后遍历
2、注意
1、如果都没有规律,就把整个sql语句包裹起来,整体循环
2、有个前提:需要开启批量sql语句执行的语句
3、修改jdbc配置
<!-- 配置数据源 -->
<dataSource type="POOLED">
<!-- 建立数据库连接的具体信息 -->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis-example?allowMultiQueries=true"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
4、还有就是collection属性。建议用@Param注解指定一个具体名字。否则难理解
5、在实际开发中,为了避免隐晦的表达造成一定的误会,建议使用@Param注解明确声明变量的名称,然后在foreach标签的collection属性中按照@Param注解指定的名称来引用传入的参数。
3、例子
1、mapper接口文件
// 根据id的批量查询
List<Employee> queryBatch(@Param("ids") List<Integer> ids);
// 根据id的批量删除
int deleteBatch(@Param("ids") List<Integer> ids);
// 批量添加
int insertBatch(@Param("list") List<Employee> employeeList);
// 批量修改
int updateBatch(@Param("list") List<Employee> employeeList);
2、sql语句
<!-- List<Employee> queryBatch(@Param("ids") List<Integer> ids);-->
<select id="queryBatch" resultType="employee">
select * from t_emp
where emp_id in
<foreach collection="ids" open="(" separator="," close=")" item="id">
#{id}
</foreach>
</select>
<!-- int deleteBatch(@Param("ids") List<Integer> ids);-->
<delete id="deleteBatch">
delete from t_emp where id in
<foreach collection="ids" open="(" close=")" item="id">
#{id}
</foreach>
</delete>
<!-- int insertBatch(@Param("list") List<Employee> employeeList);-->
<insert id="insertBatch">
insert into t_emp (emp_name,emp_salary)
values
<foreach collection="list" separator="," item="employee">
(#{employee.empName),#{employee.empSalary})
</foreach>
</insert>
<!-- int updateBatch(@Param("list") List<Employee> employeeList);-->
<update id="updateBatch">
<foreach collection="list" item="emp">
update t_emp set emp_name =#{emp.empName},emp_salary = #{emp.Salary}
where emp_id = #{emp.empId)
</foreach>
</update>
7、sql片段
1、作用
1、抽取重复的sql片段(类似封装)
2、用到\
3、\
2、例子
1、sql语句
<!-- List<Employee> queryBatch(@Param("ids") List<Integer> ids);-->
<sql id="queryBatch11">
select * from t_emp
</sql>
<select id="queryBatch1" resultType="employee">
<include refid="queryBatch11"></include>
where emp_id in
<foreach collection="ids" open="(" separator="," close=")" item="id">
#{id}
</foreach>
</select>