MyBatis(五)动态Sql

MyBatis框架的动态SQL技术是一种根据特定条件动态拼装SQL语句的功能,它存在的意义是为了解决拼接SQL语句字符串时的痛点问题。

1.if标签:

if标签可通过test属性的表达式进行判断,若表达式的结果为true,则标签中的内容会执行;反之标签中的内容不会执行。

如果我们没有去传输某些请求参数,而我在服务器中却获取了请求参数,获取的都是null。

如果我们当前提交表单的,但是我们的文本框里面什么都没有输入,我们提交到服务器中的就是空字符串" "。

在服务器中判断一下,null和"  ",没有设置条件。不等于null和" ",是设置了这个条件,就要把条件拼接到sql中。

我们设置的实体类如下所示:

package com.rgf.mybatis.pojo;

public class Emp {
    private  Integer empId;
    private  String empName;
    private  Integer age;
    private  String gender;

    public Emp(Integer empId, String empName, Integer age, String gender) {
        this.empId = empId;
        this.empName = empName;
        this.age = age;
        this.gender = gender;
    }

    public Emp() {
    }

    public Integer getEmpId() {
        return empId;
    }

    public void setEmpId(Integer empId) {
        this.empId = empId;
    }

    public String getEmpName() {
        return empName;
    }

    public void setEmpName(String empName) {
        this.empName = empName;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    @Override
    public String toString() {
        return "Emp{" +
                "empId=" + empId +
                ", empName='" + empName + '\'' +
                ", age=" + age +
                ", gender='" + gender + '\'' +
                '}';
    }
}

我们在utiles包里面封装的sqlsession如下所示:

package com.rgf.mybatis.utils;

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 java.io.IOException;
import java.io.InputStream;

public class SqlSessionUtil {
    public static SqlSession getSqlSession() {
        SqlSession sqlSession = null;
        try {
            //获取核心配置文件的输入流
            //此时我们为捕获异常,这个时候要抛的话要抛到调用他的地方。
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            //获取SqlSessionFactoryBuilder对象
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            //获取SqlSessionFactory对象
            SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
            //获取Sqlsession对象
            sqlSession = sqlSessionFactory.openSession(true);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return sqlSession;

    }
}

我们对于jdbc和核心配置的文件可以在之前的博客中进行查找。 

我们创建接口DynamicSQLMapper,在其中设置该方法:

 /**
     * 根据条件查询员工信息
     * @param emp
     * @return
     */
    List<Emp> getEmpByCondition(Emp emp);

我们设置的映射文件DynamicSQLMapper,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">
<mapper namespace="com.rgf.mybatis.mapper.DynamicSQLMapper">
    <select id="getEmpByCondition" resultType="Emp">
        select * from t_emp where
        <if test="empName !=null and empName!=''">
            emp_name=#{empName}
        </if>
        <if test="age != null and age!=''">
            and  age=#{age}
        </if>
       <if test="gender!=null and gender!=''">
           and gender=#{gender}
       </if>
    </select>
</mapper>

我们设置的测试类如下所示:

public class DynamicMapperTest {
    @Test
    public  void  testGetEmpByCondition(){
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        DynamicSQLMapper mapper = sqlSession.getMapper(DynamicSQLMapper.class);
        Emp emp = new Emp(null,"张三",20,"男");
        List<Emp> list = mapper.getEmpByCondition(emp);
        list.forEach(System.out::println);
    }
}

我们运行之后如下所示:

2.where标签

我们将测试修改如下所示:

@Test
    public  void  testGetEmpByCondition(){
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        DynamicSQLMapper mapper = sqlSession.getMapper(DynamicSQLMapper.class);
        Emp emp = new Emp(null,"",20,"男");
        List<Emp> list = mapper.getEmpByCondition(emp);
        list.forEach(System.out::println);
    }

我们将张三修改为空字符串,运行之后如下所示:

 我们看到sql语句当empName不满足的时候,直接把下一条满足的跟在了where后面。(where后面不可以直接跟and)

我们再将测试类修改如下所示:

@Test
    public  void  testGetEmpByCondition(){
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        DynamicSQLMapper mapper = sqlSession.getMapper(DynamicSQLMapper.class);
        Emp emp = new Emp(null,"",null,"");
        List<Emp> list = mapper.getEmpByCondition(emp);
        list.forEach(System.out::println);
    }

我们运行之后如下所示:

我们发现在条件都不满足的情况下,我们都不会拼接到where后面。我们的sql语句后面多出来一个where。

我们的解决方法如下所示:

解决方法一:

 我们将映射文件修改如下所示:

<!--
    动态SQL:
    1.if,通过test属性中的表达式判断标签中的内容是否有效(是否会拼接到sql中 )
    -->
    <select id="getEmpByCondition" resultType="Emp">
        select * from t_emp where 1=1
        <if test="empName !=null and empName!=''">
            and  emp_name=#{empName}
        </if>
        <if test="age != null and age!=''">
            and  age=#{age}
        </if>
       <if test="gender!=null and gender!=''">
           and gender=#{gender}
       </if>
    </select>

我们点击运行后如下所示:

我们发现把当前所有数据都查找出来了。

 我们的测试类继续修改如下所示:

 @Test
    public  void  testGetEmpByCondition(){
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        DynamicSQLMapper mapper = sqlSession.getMapper(DynamicSQLMapper.class);
        Emp emp = new Emp(null,"",20,"男");
        List<Emp> list = mapper.getEmpByCondition(emp);
        list.forEach(System.out::println);
    }

我们运行后如下所示:

 解决方法二:

我们的测试方法如下所示:

 @Test
    public  void  testGetEmpByCondition(){
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        DynamicSQLMapper mapper = sqlSession.getMapper(DynamicSQLMapper.class);
        Emp emp = new Emp(null,"张三",20,"男");
        List<Emp> list = mapper.getEmpByCondition(emp);
        list.forEach(System.out::println);
    }

我们将我们的映射文件修改如下所示:

 <select id="getEmpByCondition" resultType="Emp">
        select * from t_emp
      <where>
          <if test="empName !=null and empName!=''">
              emp_name=#{empName}
          </if>
          <if test="age != null and age!=''">
              and  age=#{age}
          </if>
          <if test="gender!=null and gender!=''">
              and gender=#{gender}
          </if>
      </where>

    </select>

我们将代码运行之后如下所示:

 如果我们将测试类进行修改:

 @Test
    public  void  testGetEmpByCondition(){
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        DynamicSQLMapper mapper = sqlSession.getMapper(DynamicSQLMapper.class);
        Emp emp = new Emp(null,"",20,"男");
        List<Emp> list = mapper.getEmpByCondition(emp);
        list.forEach(System.out::println);
    }

我们将empName改为空字符串,此时第一条if语句不成立,我们进行运行如下所示:

 我们继续进行测试:将年龄改为null,我们如下所示:

 @Test
    public  void  testGetEmpByCondition(){
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        DynamicSQLMapper mapper = sqlSession.getMapper(DynamicSQLMapper.class);
        Emp emp = new Emp(null,"",null,"");
        List<Emp> list = mapper.getEmpByCondition(emp);
        list.forEach(System.out::println);
    }

我们运行之后如下所示:

我们发现此时条件都不成立的时候,where标签没有任何功能。

where标签的功能总结如下所示:

a、若where标签中有条件成立,然后会自动生成where关键字

b、会自动将where标签中内容前多余的and去掉

c、若where标签中没有任何一个条件成立,则where标签没有任何功能

 3、trim标签

我们将测试类修改为如下所示:

@Test
    public  void  testGetEmpByCondition(){
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        DynamicSQLMapper mapper = sqlSession.getMapper(DynamicSQLMapper.class);
        Emp emp = new Emp(null,"张三",20,"");
        List<Emp> list = mapper.getEmpByCondition(emp);
        list.forEach(System.out::println);
    }

同时我们将映射文件里面的and放到内容后面,采用trim标签如下所示:

<select id="getEmpByCondition" resultType="Emp">
        select * from t_emp
        <trim prefix="where" suffixOverrides="and">
            <if test="empName !=null and empName!=''">
                emp_name=#{empName} and
            </if>
            <if test="age != null and age!=''">
                  age=#{age}  and
            </if>
            <if test="gender!=null and gender!=''">
                gender=#{gender}
            </if>
        </trim>
    </select>

其中的trim的属性如下所示: 

其中有四个属性

prefix:是在其中内容前面加上某些内容

prefixOverrides:是在我们当前内容的前面去掉指定内容

suffix:是在我们当前标签中内容的后面去添加指定内容

suffixOverrides:是在我们当前标签中内容的后面去掉指定内容 

我们运行之后如下所示:

我们发现此时where有了,后面多余的and也没有了。 

4、choose(父标签)、when(相当于if-else-if)、otherwise(相当于else)

我们创建新的方法如下所示:

 /**
     * 使用choose查询员工信息
     * @param emp
     * @return
     */
    List<Emp> getEmpByChoose(Emp emp);

我们所写的映射文件如下所示:

<select id="getEmpByChoose" resultType="Emp">
    select * from t_emp 
    <where>
        <choose>
            <when test="empName != null and empName !='' ">
                emp_name=#{empName}
            </when>
            <when test="age != null and age != ''">
                age=#{age}
            </when>
            <when test="gender != null and gender != ''">
                gender=#{gender}
            </when>
        </choose>
    </where>
</select>

 我们所写的测试类如下所示:

 @Test
    public  void  testGetEmpByChoose(){
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        DynamicSQLMapper mapper = sqlSession.getMapper(DynamicSQLMapper.class);
        Emp emp = new Emp(null,"张三",20,"");
        List<Emp> list = mapper.getEmpByChoose(emp);
        list.forEach(System.out::println);
    }

我们运行之后如下所示:

 我们发现只判断了emp_name,符合的话即停止进行判断。否则继续判断。

5、foreach标签:(批量添加)

 我们设置的接口方法如下所示:

 /**
     * 批量添加员工信息
     * @param emps
     */

    void insertMoreEmp(@Param("emps") List<Emp> emps);

我们设置的映射文件如下所示:

 <select id="insertMoreEmp" >
        <!--添加多个数据用多个小括号,中间用逗号隔开,集合先循环,然后将集合里面的每个员工信息放在相对应的字段里面进行赋值,实现批量添加-->
        insert into t_emp values
        <!--collection:用来设置我们当前要循环的集合或者数组
        item:选项的意思,用item来表示当前集合或数组中的每一个数据
        #{}:当我们传输过来的是一个实体类对象的时候,用这种方式进行访问,通过属性来访问属性值
        separator:分隔符,我们当前每一次循环的数据之间的分割符,用逗号进行分割
        -->
        <foreach collection="emps" item="emp" separator=",">
            (null,#{emp.empName},#{emp.age},#{emp.gender},null)
        </foreach>
    </select>

我们设置的测试类如下所示:

@Test
    public  void  testInsertMoreEmp(){
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        DynamicSQLMapper mapper = sqlSession.getMapper(DynamicSQLMapper.class);
        Emp emp1 = new Emp(null,"小明1",20,"男");
        Emp emp2 = new Emp(null,"小明2",20,"男");
        Emp emp3 = new Emp(null,"小明3",20,"男");
        List<Emp> list = Arrays.asList(emp1, emp2, emp3);
        mapper.insertMoreEmp(list);
    }

我们运行之后如下所示:

我们打开数据库表,我们发现成功的添加进去了:

 6、foreach标签(批量删除):

我们在mapper接口里面设置的方法如下所示:

/**如果mapper接口方法的参数是一个list或者一个数组,mybatis都会把数据放在map中的,会以List为键,list集合为值,以这个参数为值放在map中。
     *如果是数组的话,mybatis也会把它放在map里面,以array为键,以当前的参数为值
     * 批量删除的功能
     * @param empIds
     */
    void deleteMoreEmp(@Param("empIds") Integer[]empIds);

我们编写的映射文件如下所示:

 <delete id="deleteMoreEmp">
        delete from t_emp where emp_id in
        (
        <foreach collection="empIds " item="empId" separator=",">
            #{empId}
        </foreach>
        )
    </delete>

我们编写的测试类如下所示:

@Test
    public  void  testDeleteMoreEmp(){
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        DynamicSQLMapper mapper = sqlSession.getMapper(DynamicSQLMapper.class);
       Integer [] empIds=new Integer[]{6,7};
       mapper.deleteMoreEmp(empIds);
    }

我们运行之后如下所示:

 我们也可以将映射文件修改如下所示:

<delete id="deleteMoreEmp">
        delete from t_emp where emp_id in
        <!--index:当前循环的索引
        open:当前循环的所有数据以什么开始
        close:当前循环的所有数据以什么结束
        -->
        <foreach collection="empIds " item="empId" separator="," open="(" close=")">
            #{empId}
        </foreach>
    </delete>

我们也可以用另一种方式:

我们将映射文件修改如下所示:

<delete id="deleteMoreEmp" >
    delete from t_emp where
    <foreach collection="empIds" item="empId" separator="or">
 <!--separator:or(自动在前面和后面加空格的)-->
        emp_id=#{empId}
    </foreach>
</delete>

7、sql标签

我们的sql用法如下所示:

 6、sql片段
   可以记录一段sql,在需要用的地方使用include标签进行引用
   <sql id="empColumns">
    emp_id,emp_name,age,gender,dept_id
</sql>
 select <include refid="empColumns"></include> from t_emp where 1=1
    -->

 我们在映射文件里面设计如下所示:

<!--sql标签:可以将我们常用的sql片段进行一个记录,用include标签在我们需要用到的地方进行引入,refid为引用的意思-->
<sql id="empColumns">
    emp_id,emp_name,age,gender,dept_id
</sql>


    <select id="getEmpByConditionOne" resultType="Emp">
        select <include refid="empColumns"></include> from t_emp where 1=1
        <if test="empName !=null and empName!=''">
            and  emp_name=#{empName}
        </if>
        <if test="age != null and age!=''">
            and  age=#{age}
        </if>
        <if test="gender!=null and gender!=''">
            and gender=#{gender}
        </if>
    </select> 
    

我们所使用的动态sql设计总结如下所示:

 <!--
    动态SQL:
    1.if,通过test属性中的表达式判断标签中的内容是否有效(是否会拼接到sql中 )
    2.where
    a、若where标签中有条件成立,然后会自动生成where关键字
    b、会自动将where标签中内容前多余的and去掉,但是其中内容后多余的and无法去掉
    c、若where标签中没有任何一个条件成立,则where标签没有任何功能
    3.trim
    prefix、suffix:在标签中内容前面或者后面添加指定内容
    prefixOverrides、suffixOverrides:在标签中内容前面或者后面去掉指定内容
    4、choose、when、otherwise
    相当于java中的if....else if...else
    when至少设置一个,otherwise最多设置一个
    5、foreach
    collection:要循环的数组或集合
    item:用一个字符串表示数组或集合中的每一个数据
    separator:设置每次循环的数据之间的分割符
    or(自动在前面和后面加空格的)
   open:循环的所有内容以什么开始
   close:循环的所有内容以什么结束
   6、sql片段
   可以记录一段sql,在需要用的地方使用include标签进行引用
   <sql id="empColumns">
    emp_id,emp_name,age,gender,dept_id
</sql>
 select <include refid="empColumns"></include> from t_emp where 1=1
    -->
<!--sql标签:可以将我们常用的sql片段进行一个记录,用include标签在我们需要用到的地方进行引入,refid为引用的意思-->

猜你喜欢

转载自blog.csdn.net/weixin_59448049/article/details/127516614
今日推荐