文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

MyBatis动态SQL,写SQL更爽

2024-12-03 09:57

关注

MyBatis通过 OGNL 来进行动态 SQL 的使用的。目前, 动态 SQL 支持以下几种标签:

1、数据准备

为了后面的演示, 创建了一个 Maven 项目 mybatis-dynamic, 创建了对应的数据库和表 

  1. DROP TABLE IF EXISTS `student`;  
  2. CREATE TABLE `student` (  
  3.   `student_id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '编号',  
  4.   `name` varchar(20) DEFAULT NULL COMMENT '姓名',  
  5.   `phone` varchar(20) DEFAULT NULL COMMENT '电话',  
  6.   `email` varchar(50) DEFAULT NULL COMMENT '邮箱',  
  7.   `sex` tinyint(4) DEFAULT NULL COMMENT '性别',  
  8.   `locked` tinyint(4) DEFAULT NULL COMMENT '状态(0:正常,1:锁定)',  
  9.   `gmt_created` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '存入数据库的时间',  
  10.   `gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改的时间',  
  11.   `delete` int(11) DEFAULT NULL,  
  12.   PRIMARY KEY (`student_id`)  
  13. ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='学生表'

对应的项目结构

2、if 标签

if 标签是我们最常使用的。在查询、删除、更新的时候很可能会使用到。必须结合 test 属性联合使用。

2.1 在 WHERE 条件中使用 if 标签

这是常见的一种现象, 我们在进行按条件查询的时候, 可能会有多种情况。

2.1.1 查询条件

根据输入的学生信息进行条件检索

2.1.2 动态 SQL

接口函数   

  1.   
  2.        List<Student> selectByStudentSelective(Student student); 

对应的动态 SQL 

  1. <select id="selectByStudentSelective" resultMap="BaseResultMap" parameterType="com.homejim.mybatis.entity.Student">  
  2.   select  
  3.   <include refid="Base_Column_List" />  
  4.   from student  
  5.   where 11=1  
  6.   <if test="name != null and name !=''">  
  7.     and name like concat('%', #{name}, '%')  
  8.   if>  
  9.   <if test="sex != null">  
  10.     and sex=#{sex}  
  11.   if>  
  12. select> 

在此 SQL 语句中, where 1=1 是多条件拼接时的小技巧, 后面的条件查询就可以都用 and 了。

同时, 我们添加了 if 标签来处理动态 SQL   

  1. <if test="name != null and name !=''">  
  2.      and name like concat('%', #{name}, '%')  
  3.    if>  
  4.    <if test="sex != null">  
  5.      and sex=#{sex}  
  6.    if> 

此 if 标签的 test 属性值是一个符合 OGNL 的表达式, 表达式可以是 true 或 false。如果表达式返回的是数值, 则0为 false, 非 0 为 true;

2.1.3 测试   

  1. @Test  
  2.   public void selectByStudent() {  
  3.       SqlSession sqlSession = null 
  4.       sqlSession = sqlSessionFactory.openSession();  
  5.       StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);  
  6.       Student search = new Student();  
  7.       search.setName("明");  
  8.       System.out.println("只有名字时的查询");  
  9.       List<Student> studentsByName = studentMapper.selectByStudentSelective(search);  
  10.       for (int i = 0; i < studentsByName.size(); i++) {  
  11.           System.out.println(ToStringBuilder.reflectionToString(studentsByName.get(i), ToStringStyle.MULTI_LINE_STYLE));  
  12.       } 
  13.       search.setName(null);  
  14.       search.setSex((byte) 1);  
  15.       System.out.println("只有性别时的查询");  
  16.       List<Student> studentsBySex = studentMapper.selectByStudentSelective(search);  
  17.       for (int i = 0; i < studentsBySex.size(); i++) {  
  18.           System.out.println(ToStringBuilder.reflectionToString(studentsBySex.get(i), ToStringStyle.MULTI_LINE_STYLE));  
  19.       }  
  20.       System.out.println("姓名和性别同时存在的查询");  
  21.       search.setName("明");  
  22.       List<Student> studentsByNameAndSex = studentMapper.selectByStudentSelective(search);  
  23.       for (int i = 0; i < studentsByNameAndSex.size(); i++) {  
  24.           System.out.println(ToStringBuilder.reflectionToString(studentsByNameAndSex.get(i), ToStringStyle.MULTI_LINE_STYLE));  
  25.       }  
  26.       sqlSession.commit();  
  27.       sqlSession.close();  
  28.   } 

只有名字时的查询, 发送的语句和结果

查询的条件只发送了 

  1. where 11=1 and name like concat('%', ?, '%')  

只有性别时的查询, 发送的语句和结果

查询的条件只发送了 

  1. where 11=1 and sex=?  

姓名和性别同时存在的查询, 发送的语句和结果

查询条件 

  1. where 11=1 and name like concat('%', ?, '%') and sex=?  

2.2 在 UPDATE 更新列中使用 if 标签

有时候我们不希望更新所有的字段, 只更新有变化的字段。

2.2.1 更新条件

只更新有变化的字段, 空值不更新。

2.2.2动态 SQL 

  1. 接口方法  
  2.       
  3.     int updateByPrimaryKeySelective(Student record); 

对应的 SQL 

  1. <update id="updateByPrimaryKeySelective" parameterType="com.homejim.mybatis.entity.Student">  
  2.   update student  
  3.   <set>  
  4.     <if test="name != null">  
  5.       `name` = #{name,jdbcType=VARCHAR},  
  6.     if>  
  7.     <if test="phone != null">  
  8.       phone = #{phone,jdbcType=VARCHAR},  
  9.     if>  
  10.     <if test="email != null">  
  11.       email = #{email,jdbcType=VARCHAR},  
  12.     if>  
  13.     <if test="sex != null">  
  14.       sex = #{sex,jdbcType=TINYINT},  
  15.     if>  
  16.     <if test="locked != null">  
  17.       locked = #{locked,jdbcType=TINYINT},  
  18.     if>  
  19.     <if test="gmtCreated != null">  
  20.       gmt_created = #{gmtCreated,jdbcType=TIMESTAMP}, 
  21.     if>  
  22.     <if test="gmtModified != null">  
  23.       gmt_modified = #{gmtModified,jdbcType=TIMESTAMP},  
  24.     if>  
  25.   set> 
  26.   where student_id = #{studentId,jdbcType=INTEGER

2.2.3 测试 

  1. @Test  
  2.   public void updateByStudentSelective() {  
  3.       SqlSession sqlSession = null 
  4.       sqlSession = sqlSessionFactory.openSession();  
  5.       StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class); 
  6.       Student student = new Student();  
  7.       student.setStudentId(1); 
  8.       student.setName("明明");  
  9.       student.setPhone("13838438888");  
  10.       System.out.println(studentMapper.updateByPrimaryKeySelective(student));  
  11.       sqlSession.commit();  
  12.       sqlSession.close();  
  13.   } 

结果如下

2.3 在 INSERT 动态插入中使用 if 标签

我们插入数据库中的一条记录, 不是每一个字段都有值的, 而是动态变化的。在这时候使用 if 标签, 可帮我们解决这个问题。

2.3.1 插入条件

只有非空属性才插入。

2 动态SQL

接口方法 

  1.   
  2.  int insertSelective(Student record); 

对应的SQL 

  1. <insert id="insertSelective" parameterType="com.homejim.mybatis.entity.Student">  
  2.     insert into student  
  3.     <trim prefix="(" suffix=")" suffixOverrides=",">  
  4.       <if test="studentId != null">  
  5.         student_id,  
  6.       if>  
  7.       <if test="name != null">  
  8.         `name`,  
  9.       if>  
  10.       <if test="phone != null">  
  11.         phone,  
  12.       if>  
  13.       <if test="email != null">  
  14.         email,  
  15.       if>  
  16.       <if test="sex != null">  
  17.         sex,  
  18.       if>  
  19.       <if test="locked != null">  
  20.         locked, 
  21.       if>  
  22.       <if test="gmtCreated != null">  
  23.         gmt_created, 
  24.       if>  
  25.       <if test="gmtModified != null">  
  26.         gmt_modified,  
  27.       if>  
  28.     trim>  
  29.     <trim prefix="values (" suffix=")" suffixOverrides=",">  
  30.       <if test="studentId != null">  
  31.         #{studentId,jdbcType=INTEGER},  
  32.       if>  
  33.       <if test="name != null">  
  34.         #{name,jdbcType=VARCHAR},  
  35.       if>  
  36.       <if test="phone != null">  
  37.         #{phone,jdbcType=VARCHAR}, 
  38.       if>  
  39.       <if test="email != null">  
  40.         #{email,jdbcType=VARCHAR},  
  41.       if>  
  42.       <if test="sex != null">  
  43.         #{sex,jdbcType=TINYINT},  
  44.       if>  
  45.       <if test="locked != null">  
  46.         #{locked,jdbcType=TINYINT},  
  47.       if>  
  48.       <if test="gmtCreated != null">  
  49.         #{gmtCreated,jdbcType=TIMESTAMP},  
  50.       if>  
  51.       <if test="gmtModified != null">  
  52.         #{gmtModified,jdbcType=TIMESTAMP},  
  53.       if>  
  54.     trim> 
  55.   insert> 

这个 SQL 大家应该很熟悉, 毕竟是自动生成的。

2.3.3 测试   

  1. @Test  
  2.    public void insertByStudentSelective() {  
  3.        SqlSession sqlSession = null 
  4.        sqlSession = sqlSessionFactory.openSession();  
  5.        StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);  
  6.        Student student = new Student();  
  7.        student.setName("小飞机");  
  8.        student.setPhone("13838438899");  
  9.        student.setEmail("xiaofeiji@qq.com");  
  10.        student.setLocked((byte) 0);  
  11.        System.out.println(studentMapper.insertSelective(student)); 
  12.        sqlSession.commit();  
  13.        sqlSession.close();  
  14.    } 

对应的结果

SQL 中, 只有非空的字段才进行了插入。

3、choose 标签

choose when otherwise 标签可以帮我们实现 if else 的逻辑。一个 choose 标签至少有一个 when, 最多一个otherwise。

下面是一个查询的例子。

3.1 查询条件

假设 name 具有唯一性, 查询一个学生

3.2 动态SQL

接口方法 

  1.   
  2.  Student selectByIdOrName(Student record); 

对应的SQL 

  1. <select id="selectByIdOrName" resultMap="BaseResultMap" parameterType="com.homejim.mybatis.entity.Student">  
  2.    select  
  3.    <include refid="Base_Column_List" />  
  4.    from student  
  5.    where 11=1  
  6.    <choose>  
  7.      <when test="studentId != null">  
  8.        and student_id=#{studentId}  
  9.      when>  
  10.      <when test="name != null and name != ''">  
  11.        and name=#{name}  
  12.      when>  
  13.      <otherwise>  
  14.        and 1=2  
  15.      otherwise>  
  16.    choose>  
  17.  select> 

3.3 测试 

  1. @Test  
  2.     public void selectByIdOrName() {  
  3.         SqlSession sqlSession = null 
  4.         sqlSession = sqlSessionFactory.openSession();  
  5.         StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);  
  6.         Student student = new Student();  
  7.         student.setName("小飞机");  
  8.         student.setStudentId(1);  
  9.         Student studentById = studentMapper.selectByIdOrName(student);  
  10.         System.out.println("有 ID 则根据 ID 获取");  
  11.         System.out.println(ToStringBuilder.reflectionToString(studentById, ToStringStyle.MULTI_LINE_STYLE));  
  12.         student.setStudentId(null);  
  13.         Student studentByName = studentMapper.selectByIdOrName(student);  
  14.         System.out.println("没有 ID 则根据 name 获取");  
  15.         System.out.println(ToStringBuilder.reflectionToString(studentByName, ToStringStyle.MULTI_LINE_STYLE));  
  16.         student.setName(null);  
  17.         Student studentNull = studentMapper.selectByIdOrName(student);  
  18.         System.out.println("没有 ID 和 name, 返回 null");  
  19.         Assert.assertNull(studentNull); 
  20.         sqlSession.commit();  
  21.         sqlSession.close();  
  22.     } 

有 ID 则根据 ID 获取, 结果

没有 ID 则根据 name 获取

没有 ID 和 name, 返回 null

4、trim(set、where)

这三个其实解决的是类似的问题。如我们在写前面的[在 WHERE 条件中使用 if 标签] SQL 的时候, where 1=1 这个条件我们是不希望存在的。

4.1 where

4.1.1 查询条件

根据输入的学生信息进行条件检索。

不使用 where 1=1。

4.1.2 动态 SQL

很显然, 我们要解决这几个问题

当条件都不满足时:此时 SQL 中应该要不能有 where , 否则导致出错

当 if 有条件满足时:SQL 中需要有 where, 且第一个成立的 if 标签下的 and | or 等要去掉

这时候, 我们可以使用 where 标签。

接口方法 

  1.   
  2. List<Student> selectByStudentSelectiveWhereTag(Student student); 

对应的 SQL 

  1. <select id="selectByStudentSelectiveWhereTag" resultMap="BaseResultMap" parameterType="com.homejim.mybatis.entity.Student">  
  2.     select  
  3.     <include refid="Base_Column_List" />  
  4.     from student  
  5.    <where>  
  6.     <if test="name != null and name !=''">  
  7.       and name like concat('%', #{name}, '%')  
  8.     if>  
  9.     <if test="sex != null">  
  10.       and sex=#{sex}  
  11.     if>  
  12.    where>  
  13.   select> 

4.1.3 测试   

  1. @Test  
  2.    public void selectByStudentWhereTag() {  
  3.        SqlSession sqlSession = null 
  4.        sqlSession = sqlSessionFactory.openSession();  
  5.        StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);  
  6.        Student search = new Student();  
  7.        search.setName("明");  
  8.        System.out.println("只有名字时的查询");  
  9.        List<Student> studentsByName = studentMapper.selectByStudentSelectiveWhereTag(search);  
  10.        for (int i = 0; i < studentsByName.size(); i++) {  
  11.            System.out.println(ToStringBuilder.reflectionToString(studentsByName.get(i), ToStringStyle.MULTI_LINE_STYLE));  
  12.        }  
  13.        search.setSex((byte) 1);  
  14.        System.out.println("姓名和性别同时存在的查询");  
  15.        List<Student> studentsBySex = studentMapper.selectByStudentSelectiveWhereTag(search);  
  16.        for (int i = 0; i < studentsBySex.size(); i++) {  
  17.            System.out.println(ToStringBuilder.reflectionToString(studentsBySex.get(i), ToStringStyle.MULTI_LINE_STYLE));  
  18.        }  
  19.        System.out.println("姓名和性别都不存在时查询");  
  20.        search.setName(null);  
  21.        search.setSex(null); 
  22.        List<Student> studentsByNameAndSex = studentMapper.selectByStudentSelectiveWhereTag(search);  
  23.        for (int i = 0; i < studentsByNameAndSex.size(); i++) { 
  24.            System.out.println(ToStringBuilder.reflectionToString(studentsByNameAndSex.get(i), ToStringStyle.MULTI_LINE_STYLE));  
  25.        }  
  26.        sqlSession.commit();  
  27.        sqlSession.close();  
  28.    } 

只有名字时的查询, 有 where

姓名和性别同时存在的查询, 有 where

姓名和性别都不存在时查询, 此时, where 不会再出现了。

4.2 set

set 标签也类似, 在 [2.2 在 UPDATE 更新列中使用 if 标签] 中, 如果我们的方法 updateByPrimaryKeySelective 没有使用

4.3 trim

set 和 where 其实都是 trim 标签的一种类型, 该两种功能都可以使用 trim 标签进行实现。

4.3.1 trim 来表示 where

如以上的 where 标签, 我们也可以写成 

  1. <trim prefix="where" prefixOverrides="AND |OR">  
  2. trim> 

表示当 trim 中含有内容时, 添加 where, 且第一个为 and 或 or 时, 会将其去掉。而如果没有内容, 则不添加 where。

4.3.2 trim 来表示 set

相应的, set 标签可以如下表示 

  1. <trim prefix="SET" suffixOverrides=",">  
  2. trim> 

表示当 trim 中含有内容时, 添加 set, 且最后的内容为 , 时, 会将其去掉。而没有内容, 不添加 set

4.3.3 trim 的几个属性

5、foreach 标签

foreach 标签可以对数组, Map 或实现 Iterable 接口。

foreach 中有以下几个属性:

其他的比较好理解, collection 中的值应该怎么设定呢?

跟接口方法中的参数相关。

1. 只有一个数组参数或集合参数

默认情况:集合collection=list, 数组是collection=array

推荐:使用 @Param 来指定参数的名称, 如我们在参数前@Param("ids"), 则就填写 collection=ids

2. 多参数

多参数请使用 @Param 来指定, 否则SQL中会很不方便

3. 参数是Map

指定为 Map 中的对应的 Key 即可。其实上面的 @Param 最后也是转化为 Map 的。

4. 参数是对象

使用属性.属性即可。

5.1 在 where 中使用 foreach

在 where条件中使用, 如按id集合查询, 按id集合删除等。

5.1.1 查询条件

我们希望查询用户 id 集合中的所有用户信息。

5.1.2 动态 SQL

函数接口   

  1.   
  2.    List<Student> selectByStudentIdList(List<Integer> ids); 

对应 SQL 

  1. <select id="selectByStudentIdList" resultMap="BaseResultMap">  
  2.     select  
  3.     <include refid="Base_Column_List" />  
  4.     from student  
  5.     where student_id in  
  6.     <foreach collection="list" item="id" open="(" close=")" separator="," index="i"> 
  7.       #{id}  
  8.     foreach>  
  9.   select> 

5.1.3 测试 

  1. @Test  
  2.   public void selectByStudentIdList() {  
  3.       SqlSession sqlSession = null 
  4.       sqlSession = sqlSessionFactory.openSession();  
  5.       StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);  
  6.       List<Integer> ids = new LinkedList<>();  
  7.       ids.add(1);  
  8.       ids.add(3);  
  9.       List<Student> students = studentMapper.selectByStudentIdList(ids);  
  10.       for (int i = 0; i < students.size(); i++) {  
  11.           System.out.println(ToStringBuilder.reflectionToString(students.get(i), ToStringStyle.MULTI_LINE_STYLE));  
  12.       }  
  13.       sqlSession.commit();  
  14.       sqlSession.close();  
  15.   } 

结果

5.2 foreach 实现批量插入

可以通过foreach来实现批量插入。

5.2.1 动态SQL

接口方法 

  1.   
  2. int insertList(List<Student> students); 

对应的SQL 

  1. <insert id="insertList">  
  2.   insert into student(name, phone, email, sex, locked)  
  3.   values  
  4.   <foreach collection="list" item="student" separator=",">  
  5.     (  
  6.     #{student.name}, #{student.phone},#{student.email},  
  7.     #{student.sex},#{student.locked}  
  8.     )  
  9.   foreach>  
  10. insert> 

5.2.2 测试 

  1. @Test  
  2.   public void insertList() {  
  3.       SqlSession sqlSession = null 
  4.       sqlSession = sqlSessionFactory.openSession();  
  5.       StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);  
  6.       List<Student> students = new LinkedList<>();  
  7.       Student stu1 = new Student(); 
  8.       stu1.setName("批量01");  
  9.       stu1.setPhone("13888888881");  
  10.       stu1.setLocked((byte) 0);  
  11.       stu1.setEmail("13888888881@138.com");  
  12.       stu1.setSex((byte) 1);  
  13.       students.add(stu1);  
  14.       Student stu2 = new Student();  
  15.       stu2.setName("批量02");  
  16.       stu2.setPhone("13888888882");  
  17.       stu2.setLocked((byte) 0);  
  18.       stu2.setEmail("13888888882@138.com");  
  19.       stu2.setSex((byte) 0);  
  20.       students.add(stu2);  
  21.       System.out.println(studentMapper.insertList(students));  
  22.       sqlSession.commit();  
  23.       sqlSession.close();  
  24.   } 

结果

6、bind 标签

bind 标签是通过 OGNL 表达式去定义一个上下文的变量, 这样方便我们使用。

如在 selectByStudentSelective 方法中, 有如下 

  1. <if test="name != null and name !=''">  
  2.       and name like concat('%', #{name}, '%')  
  3.     if> 

在 MySQL 中, 该函数支持多参数, 但在 Oracle 中只支持两个参数。那么我们可以使用 bind 来让该 SQL 达到支持两个数据库的作用 

  1. <if test="name != null and name !=''">  
  2.      <bind name="nameLike" value="'%'+name+'%'"/>  
  3.      and name like #{nameLike}  
  4. if> 

更改后的查询结果如下

7、代码

使用示例:

https://github.com/homejim/mybatis-examples 

 

来源:数据库开发内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯