MyBatis使用篇(四)—— Mapper配置动态SQL

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_36378917/article/details/85059459

1、综述

  在Mapper配置文件中,有时候需要根据一些查询条件来选择不同的SQL语句,或者将一些使用频率极高的SQL语句单独配置,在需要的地方引用。MyBatis提供了一种可以根据条件动态配置SQL语句,以及单独配置SQL语句块的机制。动态SQL,即通过MyBatis提供的各种标签对条件作出判断以实现动态拼接SQL语句。这里的条件判断使用的表达式为OGNL表达式。

2、测试环境搭建

2.1 创建数据库表

  在之前测试使用的mybatis数据库中创建student表,创建表的SQL语句及添加测试数据如下:

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for `student`
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
  `id` int(5) NOT NULL auto_increment,
  `name` varchar(20) default NULL,
  `age` int(3) default NULL,
  `score` double default NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of student
-- ----------------------------
INSERT INTO `student` VALUES ('1', '张三', '23', '93.5');
INSERT INTO `student` VALUES ('2', '李四', '24', '94.5');
INSERT INTO `student` VALUES ('3', '王五', '25', '92.5');

2.2 定义实体类

  在com.ccff.mybatis.model下定义Student实体类,具体代码如下所示:

package com.ccff.mybatis.model;

public class Student {
    private int id;
    private String name;
    private int age;
    private double score;

    public Student() {
    }

    public Student(String name, int age, double score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

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

    public double getScore() {
        return score;
    }

    public void setScore(double score) {
        this.score = score;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                '}';
    }
}

2.3 定义接口DAO

  在com.ccff.mybatis.dao下定义接口IStudentDao,代码如下:

package com.ccff.mybatis.dao;

import com.ccff.mybatis.model.Student;

import java.util.List;

public interface IStudentDao {
    //用于测试if标签
    List<Student> selectStudentsIf(Student student);

    //用于测试where标签
    List<Student> selectStudentsWhere(Student student);

    //用于测试choose标签
    List<Student> selectStudentsChoose(Student student);

    //用于测试foreach标签
    List<Student> selectStudentsForeachArray(Object[] studentIds);
    List<Student> selectStudentsForeachList(List<Integer> studentIds);
    List<Student> selectStudentsForeachList2(List<Student> students);

    //用于测试sql标签
    List<Student> selectStudentsBySQLFragment(List<Student> students);
}

2.4 定义接口实现类

在com.ccff.mybatis.dao下定义接口IStudentDao的实现类IStudentImpl,代码如下:

package com.ccff.mybatis.dao;

import com.ccff.mybatis.datasource.DataConnection;
import com.ccff.mybatis.model.Student;
import org.apache.ibatis.session.SqlSession;

import java.io.IOException;
import java.util.List;

public class IStudentImpl implements IStudentDao {
    private SqlSession sqlSession;
    private DataConnection dataConnection;

    public IStudentImpl(){
        super();
        try {
            DataConnection dataConnection = new DataConnection();
            sqlSession = dataConnection.getSqlSession();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public List<Student> selectStudentsIf(Student student) {
        return null;
    }

    @Override
    public List<Student> selectStudentsWhere(Student student) {
        return null;
    }

    @Override
    public List<Student> selectStudentsChoose(Student student) {
        return null;
    }

    @Override
    public List<Student> selectStudentsForeachArray(Object[] studentIds) {
        return null;
    }

    @Override
    public List<Student> selectStudentsForeachList(List<Integer> studentIds) {
        return null;
    }

    @Override
    public List<Student> selectStudentsForeachList2(List<Student> students) {
        return null;
    }

    @Override
    public List<Student> selectStudentsBySQLFragment(List<Student> students) {
        return null;
    }

    private void close(){
        sqlSession.close();
    }
}

2.5 定义测试类

  在com.ccff.mybatis.test下定义测试类StudentTest,代码如下:

package com.ccff.mybatis.test;

import com.ccff.mybatis.dao.IStudentDao;
import com.ccff.mybatis.dao.IStudentImpl;

public class StudentTest {
    private IStudentDao studentDao;

    public StudentTest(){
        super();
        studentDao = new IStudentImpl();
    }
}

2.6 定义StudentMapper映射文件

  在config/sqlmap下创建名为“StudentMapper”的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">

<mapper namespace="StudentTest">
    
</mapper>

2.7 注意事项

  在Mapper的动态SQL中若出现大于号、小于号、大于等于号和小于等于号,最好将其转换为实体符号。否则XML可能会出现解析出错问题。特别是对小于号(<),在XML中是绝对不能出现的。否则,一定出错。具体的对应转换如下图所示:
在这里插入图片描述

3、if标签

  对于该标签的执行,当test的值为true时,会将包含的SQL片段拼接到其所在的SQL语句中。

扫描二维码关注公众号,回复: 5339448 查看本文章

  本例实现的功能是:查询出满足用户提交查询条件的所有学生。用户提交的查询条件可以包含一个姓名的模糊查询,同时还可以包含一个年龄的下限。当然,用户在提交表单时可能两个条件均做出了设定,也可能两个条件均不做设定,也可以只做其中一项设定。

  这引发的问题是,查询条件不确定,查询条件依赖于用户提交的内容。此时,就可使用动态SQL语句,根据用户提交内容对将要执行的SQL进行拼接。

  首先,修改Mapper映射文件,添加id为“selectStudentsIf”的select标签,具体代码如下:

	<!--if-->
    <select id="selectStudentsIf" resultType="Student">
        select * from student where 1=1
        <if test="name != null and name != ''">
            and name like '%' #{name} '%'
        </if>
        <if test="age > 0">
            and age > #{age}
        </if>
    </select>

  其次,修改接口实现类IStudentImpl中的selectStudentsIf如下:

	@Override
    public List<Student> selectStudentsIf(Student student) {
        List<Student> students = null;
        students = sqlSession.selectList("StudentTest.selectStudentsIf",student);
        this.close();
        return students;
    }

  最后,修改测试类StudentTest,添加测试方法TestSelectStudentsIf如下:

	@Test
    public void TestSelectStudentsIf(){
        Student student = new Student("李",23,0);
        List<Student> students = studentDao.selectStudentsIf(student);
        System.out.println(students);
    }

  当前student数据表中的数据如下所示:
  在这里插入图片描述
  当运行测试方法后,能够在控制台输出name中包括“李”且age大于23的所有student。
在这里插入图片描述

4、where标签

  if标签中存在一个比较麻烦的地方:需要在where后手工添加1=1子句。因为,若where后的所有if条件均为false,而where后又没有1=1子句,则SQL中就只会剩下一个空的where,SQL出错。所以,在where后,需要添加永远为真的子句1=1,以防止这种情况的发生。但当数据量很大时,会严重影响查询效率。

  使用where标签,在有查询条件时,可以自动添加上where子句;没有查询条件时,不会添加where子句。需要注意的是,第一个if标签中的SQL片段,可以不包含and。不过,写上and也不错,系统会自动将多余的and去掉。但其他if标签中SQL片段的and是必须写上的,否则SQL语句会拼接出错。

  首先,修改Mapper映射文件,添加id为“selectStudentsWhere”的select标签,SQL语句的功能与测试if标签的SQL语句功能一致。具体代码如下:

	<!--where-->
    <select id="selectStudentsWhere" resultType="Student">
        select * from student
        <where>
            <if test="name != null and name != ''">
                and name like '%' #{name} '%'
            </if>
            <if test="age > 0">
                and age > #{age}
            </if>
        </where>
    </select>

  其次,修改接口实现类IStudentImpl中的selectStudentsWhere如下:

	@Override
    public List<Student> selectStudentsWhere(Student student) {
        List<Student> students = null;
        students = sqlSession.selectList("StudentTest.selectStudentsWhere",student);
        this.close();
        return students;
    }

  最后,修改测试类StudentTest,添加测试方法TestSelectStudentsWhere如下:

	@Test
    public void TestSelectStudentsWhere(){
        Student student = new Student("",23,0);
        List<Student> students = studentDao.selectStudentsWhere(student);
        System.out.println(students);
    }

  当前student数据表中的数据如下所示:
  在这里插入图片描述
  当运行测试方法后,能够在控制台输出age大于23的所有student。
在这里插入图片描述

5、choose标签

  choose标签中只可以包含when、otherwise标签,可以包含多个when和一个otherwise。它们联合使用,完成Java中的开关语句switch/case功能。

  本例完成的需求是:若姓名不为空,按照姓名查询;若姓名为空,则按照年龄查询;若没有查询条件,则没有查询结果。

  首先,修改Mapper映射文件,添加id为“selectStudentsWhere”的select标签,具体代码如下:

	<!--choose-->
    <select id="selectStudentsChoose" resultType="Student">
        select * from student
        <where>
            <choose>
                <when test="name != null and name != ''">
                    and name like '%' #{name} '%'
                </when>
                <when test="age > 0">
                    and age &lt; #{age}
                </when>
                <otherwise>
                    and 1 != 1
                </otherwise>
            </choose>
        </where>
    </select>

  对于choose标签,其会从第一个when标签开始逐个向后进行条件判断。若出现when标签中的test属性值为true的情况,则直接结束choose标签,不再向后进行判断查找。若所有的when标签的test结果判断均为false,则最后会执行otherwise标签。

  其次,修改接口实现类IStudentImpl中的selectStudentsChoose如下:

	@Override
    public List<Student> selectStudentsChoose(Student student) {
        List<Student> students = null;
        students = sqlSession.selectList("StudentTest.selectStudentsChoose",student);
        this.close();
        return students;
    }

  最后,修改测试类StudentTest,添加测试方法TestSelectStudentsChoose如下:

	@Test
    public void TestSelectStudentsChoose(){
        Student student = new Student("",0,0);
        List<Student> students = studentDao.selectStudentsChoose(student);
        System.out.println(students);
    }

  当前student数据表中的数据如下所示:
  在这里插入图片描述
  当运行测试方法后,由于name为空,年龄为0,因此前两个when标签的test属性均为false,将执行otherwise标签中的条件,即无查询结果数据。
在这里插入图片描述

6、foreach标签

  以下三组均实现需求:查询出id为1和3的学生信息。

6.1 遍历数组

  首先,修改Mapper映射文件,添加id为“selectStudentsForeachArray”的select标签,具体代码如下:

	<!--foreach array-->
    <select id="selectStudentsForeachArray" resultType="Student">
        select * from student
        <if test="array != null and array.length > 0">
            where id in
            <foreach collection="array" open="(" close=")" item="myid" separator=",">
                #{myid}
            </foreach>
        </if>
    </select>

  在使用foereach标签时,需要注意的是:

  • collection表示要遍历的集合类型。其中array表示数组,list表示集合。
  • open、close、separator为对遍历内容的SQL拼接。
  • 动态SQL的判断中使用的都是OGNL表达式。OGNL表达式中的数组使用array表示,数组长度使用array.length表示。

  其次,修改接口实现类IStudentImpl中的selectStudentsForeachArray如下:

@Override
    public List<Student> selectStudentsForeachArray(Object[] studentIds) {
        List<Student> students = null;
        students = sqlSession.selectList("StudentTest.selectStudentsForeachArray",studentIds);
        this.close();
        return students;
    }

  最后,修改测试类StudentTest,添加测试方法TestSelectStudentsForeachArray如下:

	@Test
    public void TestSelectStudentsForeachArray(){
        Object[] studentIds = new Object[]{1,3};
        List<Student> students = studentDao.selectStudentsForeachArray(studentIds);
        System.out.println(students);
    }

  当前student数据表中的数据如下所示:
  在这里插入图片描述
  当运行测试方法后,则可以在控制台看到id为1和3的学生信息。
在这里插入图片描述

6.2 遍历泛型为基本类型的List

  首先,修改Mapper映射文件,添加id为“selectStudentsForeachList”的select标签,具体代码如下:

	<!--foreach List<Integer>-->
    <select id="selectStudentsForeachList" resultType="Student">
        select * from student
        <if test="list != null and list.size > 0">
            where id in
            <foreach collection="list" open="(" close=")" item="myid" separator=",">
                #{myid}
            </foreach>
        </if>
    </select>

  其次,修改接口实现类IStudentImpl中的selectStudentsForeachList如下:

	@Override
    public List<Student> selectStudentsForeachList(List<Integer> studentIds) {
        List<Student> students = null;
        students = sqlSession.selectList("StudentTest.selectStudentsForeachList",studentIds);
        this.close();
        return students;
    }

  最后,修改测试类StudentTest,添加测试方法TestSelectStudentsForeachList如下:

	@Test
    public void TestSelectStudentsForeachList(){
        List<Integer> studentIds = new ArrayList<>();
        studentIds.add(1);
        studentIds.add(3);
        List<Student> students = studentDao.selectStudentsForeachList(studentIds);
        System.out.println(students);
    }

  当前student数据表中的数据如下所示:
  在这里插入图片描述
  当运行测试方法后,则可以在控制台看到id为1和3的学生信息。
在这里插入图片描述

6.3 遍历泛型为自定义类型的List

  首先,修改Mapper映射文件,添加id为“selectStudentsForeachList2”的select标签,这里需要注意的是当前遍历对象类型时List中的泛型,即是Student对象。具体代码如下:

	<!--foreach List<Student>-->
    <select id="selectStudentsForeachList2" resultType="Student">
        select * from student
        <if test="list != null and list.size > 0">
            where id in
            <foreach collection="list" open="(" close=")" item="stu" separator=",">
                #{stu.id}
            </foreach>
        </if>
    </select>

  其次,修改接口实现类IStudentImpl中的selectStudentsForeachList2如下:

@Override
    public List<Student> selectStudentsForeachList2(List<Student> students) {
        List<Student> studentRes = null;
        studentRes = sqlSession.selectList("StudentTest.selectStudentsForeachList2",students);
        this.close();
        return studentRes;
    }

  最后,修改测试类StudentTest,添加测试方法TestSelectStudentsForeachList2如下:

	@Test
    public void TestSelectStudentsForeachList2(){
        List<Student> students = new ArrayList<>();
        Student student1 = new Student();
        student1.setId(1);
        Student student2 = new Student();
        student2.setId(3);
        students.add(student1);
        students.add(student2);
        List<Student> studentRes = studentDao.selectStudentsForeachList2(students);
        System.out.println(studentRes);
    }

  当前student数据表中的数据如下所示:
  在这里插入图片描述
  当运行测试方法后,则可以在控制台看到id为1和3的学生信息。
在这里插入图片描述

7、sql标签

  sql标签用于定义SQL片段,以便其他SQL标签复用。而其他标签使用该SQL片段,需要使用include子标签。该sql标签可以定义SQL语句中的任何部分,所以include子标签可以放在动态SQL的任何位置。

  本例将在6.3小节的基础上进行修改,将SQL语句中的“select * from student”部分抽取出来形成独立的SQL片段,以便其他SQL复用。

  首先,修改Mapper映射文件,添加id为“selectStudentHead”的sql标签,具体代码如下:

	<!--sql-->
	<!--定义sql片段-->
    <sql id="selectStudentHead">
        select * from student
    </sql>

  其次,修改Mapper映射文件,添加id为“selectStudentsBySQLFragment”的select标签,具体代码如下:

	<select id="selectStudentsBySQLFragment" resultType="Student">
        <!-- 使用sql片段 -->
        <include refid="selectStudentHead" />
        <if test="list != null and list.size > 0">
            where id in
            <foreach collection="list" open="(" close=")" item="stu" separator=",">
                #{stu.id}
            </foreach>
        </if>
    </select>

  然后,修改接口实现类IStudentImpl中的selectStudentsBySQLFragment如下:

@Override
    public List<Student> selectStudentsBySQLFragment(List<Student> students) {
        List<Student> studentRes = null;
        studentRes = sqlSession.selectList("StudentTest.selectStudentsBySQLFragment",students);
        this.close();
        return studentRes;
    }

  最后,修改测试类StudentTest,添加测试方法TestSelectStudentsBySQLFragment如下:

	@Test
    public void TestSelectStudentsBySQLFragment(){
        List<Student> students = new ArrayList<>();
        Student student1 = new Student();
        student1.setId(1);
        Student student2 = new Student();
        student2.setId(3);
        students.add(student1);
        students.add(student2);
        List<Student> studentRes = studentDao.selectStudentsBySQLFragment(students);
        System.out.println(studentRes);
    }

  当前student数据表中的数据如下所示:
  在这里插入图片描述
  当运行测试方法后,则可以在控制台看到id为1和3的学生信息。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_36378917/article/details/85059459