MyBatis使用篇 Mapper配置动态SQL

2022-06-28 12:18:36

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`-- ----------------------------DROPTABLEIFEXISTS`student`;CREATETABLE`student`(`id`int(5)NOTNULLauto_increment,`name`varchar(20)defaultNULL,`age`int(3)defaultNULL,`score`doubledefaultNULL,PRIMARYKEY(`id`))ENGINE=InnoDBDEFAULTCHARSET=utf8;-- ------------------------------ Records of student-- ----------------------------INSERTINTO`student`VALUES('1','张三','23','93.5');INSERTINTO`student`VALUES('2','李四','24','94.5');INSERTINTO`student`VALUES('3','王五','25','92.5');

2.2 定义实体类

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

package com.ccff.mybatis.model;publicclassStudent{privateint id;private String name;privateint age;privatedouble score;publicStudent(){}publicStudent(String name,int age,double score){this.name= name;this.age= age;this.score= score;}publicintgetId(){return id;}publicvoidsetId(int id){this.id= id;}public StringgetName(){return name;}publicvoidsetName(String name){this.name= name;}publicintgetAge(){return age;}publicvoidsetAge(int age){this.age= age;}publicdoublegetScore(){return score;}publicvoidsetScore(double score){this.score= score;}@Overridepublic StringtoString(){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;publicinterfaceIStudentDao{//用于测试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;publicclassIStudentImplimplementsIStudentDao{private SqlSession sqlSession;private DataConnection dataConnection;publicIStudentImpl(){super();try{
            DataConnection dataConnection=newDataConnection();
            sqlSession= dataConnection.getSqlSession();}catch(IOException e){
            e.printStackTrace();}}@Overridepublic List<Student>selectStudentsIf(Student student){return null;}@Overridepublic List<Student>selectStudentsWhere(Student student){return null;}@Overridepublic List<Student>selectStudentsChoose(Student student){return null;}@Overridepublic List<Student>selectStudentsForeachArray(Object[] studentIds){return null;}@Overridepublic List<Student>selectStudentsForeachList(List<Integer> studentIds){return null;}@Overridepublic List<Student>selectStudentsForeachList2(List<Student> students){return null;}@Overridepublic List<Student>selectStudentsBySQLFragment(List<Student> students){return null;}privatevoidclose(){
        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;publicclassStudentTest{private IStudentDao studentDao;publicStudentTest(){super();
        studentDao=newIStudentImpl();}}

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"><mappernamespace="StudentTest"></mapper>

2.7 注意事项

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

3、if标签

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

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

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

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

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

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

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

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

@TestpublicvoidTestSelectStudentsIf(){
        Student student=newStudent("李",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--><selectid="selectStudentsWhere"resultType="Student">
        select * from student<where><iftest="name != null and name !=''">
                and name like '%' #{name} '%'</if><iftest="age > 0">
                and age > #{age}</if></where></select>

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

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

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

@TestpublicvoidTestSelectStudentsWhere(){
        Student student=newStudent("",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--><selectid="selectStudentsChoose"resultType="Student">
        select * from student<where><choose><whentest="name != null and name !=''">
                    and name like '%' #{name} '%'</when><whentest="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如下:

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

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

@TestpublicvoidTestSelectStudentsChoose(){
        Student student=newStudent("",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--><selectid="selectStudentsForeachArray"resultType="Student">
        select * from student<iftest="array != null and array.length > 0">
            where id in<foreachcollection="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如下:

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

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

@TestpublicvoidTestSelectStudentsForeachArray(){
        Object[] studentIds=newObject[]{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>--><selectid="selectStudentsForeachList"resultType="Student">
        select * from student<iftest="list != null and list.size > 0">
            where id in<foreachcollection="list"open="("close=")"item="myid"separator=",">
                #{myid}</foreach></if></select>

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

@Overridepublic List<Student>selectStudentsForeachList(List<Integer> studentIds){
        List<Student> students= null;
        students= sqlSession.selectList("StudentTest.selectStudentsForeachList",studentIds);this.close();return students;
  • 作者:0 errors 0 warnings
  • 原文链接:https://blog.csdn.net/weixin_36378917/article/details/85059459
    更新时间:2022-06-28 12:18:36