MyBatis(五)MyBatis动态SQL

摘要:
MyBatis目录MyBatis(I)MyBatis初识-绿雪松-博客花园MyBatisMyBatis的强大功能。MyBatis还可以在注释中配置SQL。然而,由于注释函数有限,复杂SQL语句的可读性较差,因此很少使用它。MyBatis的动态SQL包含以下元素,如下表所示。Choose、when和otherwise标记MyBatis中的动态语句Choose when otherwise类似于Java中的switch-case默认语句。
MyBatis 目录 MyBatis 动态SQL

​ 动态 SQLMyBatis 的强大特性之一。在 JDBC 或其它类似的框架中,开发人员通常需要手动拼接 SQL语句。根据不同的条件拼接 SQL语句是一件极其痛苦的工作。例如,拼接时要确保添加了必要的空格,还要注意去掉列表最后一个列名的逗号。而动态 SQL恰好解决了这一问题,可以根据场景动态的构建查询。

​ 动态 SQL只有几个基本元素,与 JSTL 或 XML 文本处理器相似,十分简单明了,大量的判断都可以在 MyBatis 的映射 XML 文件里配置,以达到许多需要大量代码才能实现的功能。

​ 动态 SQL大大减少了编写代码的工作量,更体现了 MyBatis 的灵活性、高度可配置性和可维护性。

MyBatis 也可以在注解中配置 SQL,但是由于注解功能受限,且对于复杂的 SQL 语句来说可读性差,所以使用较少。本教程不对它们进行介绍。

MyBatis 的动态 SQL 包括以下几种元素,如下表所示。

元素作用备注
if判断语句单条件分支判断
choose(when、otherwise)相当于 Java 中的 switch case 语句多条件分支判断
trimwhere辅助元素用于处理一些SQL拼装问题
foreach循环语句在in语句等列举条件常用
bind辅助元素拼接参数

数据结构

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` int NOT NULL AUTO_INCREMENT,
  `username` varchar(20),
  `sex` varchar(20),
	`birthday` date,
	`address` varchar(100),
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

添加一些数据

INSERT INTO user(username,sex,birthday,address) value("张三","男","2000-10-10","四川成都");
INSERT INTO user(username,sex,birthday,address) value("李四","男","2000-10-10","四川成都");
INSERT INTO user(username,sex,birthday,address) value("王五","男","2000-10-10","四川成都");

User类

package org.mybatis.example;

import lombok.Data;
import lombok.ToString;

import java.util.List;

@Data
@ToString
public class User {
    private int id;
    private String username;
    private String sex;
    private String birthday;
    private String address;
}

resultMap

<resultMap type="org.mybatis.example.User" id="myResult">
        <id property="id" column="id" />
        <result property="username" column="username" />
        <result property="sex" column="sex" />
        <result property="birthday" column="birthday" />
        <result property="address" column="address" />
    </resultMap>
if标签

MyBatis if 类似于 Java 中的 if 语句,是 MyBatis 中最常用的判断语句。使用 if 标签可以节省许多拼接 SQL 的工作,把精力集中在 XML 的维护上。

if 语句使用方法简单,常常与 test 属性联合使用。语法如下。

<if test="判断条件">
    SQL语句
</if>

当判断条件为 true 时,才会执行所包含的 SQL 语句。

最常见的场景是在 if 语句中包含 where 子句,例如。

<select   resultMap="myResult">
    select * from user
    <if test="name != null">
        where name like #{name}
    </if>
</select>

以上表示如果传入的name不为空就执行where name like #{name}语句 如果为空就只执行select * from user

可多个 if 语句同时使用。

<select    parameterType="org.mybatis.example.User" resultMap="myResult">
    select * from user where
        <if test="username != null">
           username=#{username}
        </if>
         
        <if test="username != null">
           and sex=#{sex}
        </if>
</select>

这样写我们可以看到,如果 sex 等于 null,那么查询语句为 select * from user where username=#{username},但是如果usename 为空呢?那么查询语句为 select * from user where and sex=#{sex},这是错误的 SQL 语句,如何解决呢?请看下面的 where 语句

两种方式

1、 万能1=1 where 后面加上 1=1

2、if+where 语句

where 标签
<select    parameterType="org.mybatis.example.User" resultMap="myResult">
    select * from user
    <where>
        <if test="username != null">
           username=#{username}
        </if>
         
        <if test="username != null">
           and sex=#{sex}
        </if>
    </where>
</select>

​ 这个where标签会知道如果它包含的标签中有返回值的话,它就插入一个where。此外,如果标签返回的内容是以AND 或OR 开头的,则它会剔除掉。

choose、when和otherwise标签

MyBatis 中动态语句 choose-when-otherwise 类似于 Java 中的 switch-case-default 语句。由于 MyBatis 并没有为 if 提供对应的 else 标签,如果想要达到<if>...<else>...</else>`` </if> 的效果,可以借助 <choose><when><otherwise> 来实现。

动态语句 choose-when-otherwise 语法如下。

<choose>
    <when test="判断条件1">
        SQL语句1
    </when >
    <when test="判断条件2">
        SQL语句2
    </when >
    <when test="判断条件3">
        SQL语句3
    </when >
    <otherwise>
        SQL语句4
    </otherwise>
</choose>

choose 标签按顺序判断其内部 when 标签中的判断条件是否成立,如果有一个成立,则执行相应的 SQL 语句,choose 执行结束;如果都不成立,则执行 otherwise 中的 SQL 语句。这类似于 Javaswitch 语句,chooseswitchwhencaseotherwise 则为 default

<select   parameterType="org.mybatis.example.User" resultMap="myResult">
      select * from user
      <where>
          <choose>
              <when test="id !='' and id != null">
                  id=#{id}
              </when>
              <when test="username !='' and username != null">
                  and username=#{username}
              </when>
              <otherwise>
                  and sex=#{sex}
              </otherwise>
          </choose>
      </where>
  </select>

  也就是说,这里我们有三个条件,id,username,sex,只能选择一个作为查询条件

    如果 id 不为空,那么查询语句为:select * from user where id=?

    如果 id 为空,那么看username 是否为空,如果不为空,那么语句为 select * from user where username=?;

          如果 username 为空,那么查询语句为 select * from user where sex=?

set标签

在 Mybatis 中,update 语句可以使用 set 标签动态更新列。set 标签可以为 SQL 语句动态的添加 set 关键字,剔除追加到条件末尾多余的逗号。

<!-- 根据 id 更新 user 表的数据 -->
<update   parameterType="org.mybatis.example.User">
    update user u
        <set>
            <if test="username != null and username != ''">
                u.username = #{username},
            </if>
            <if test="sex != null and sex != ''">
                u.sex = #{sex}
            </if>
        </set>
     
     where id=#{id}
</update>

 这样写,如果第一个条件 username 为空,那么 sql 语句为:update user u set u.sex=? where id=?

      如果第一个条件不为空,那么 sql 语句为:update user u set u.username = ? ,u.sex = ? where id=?

foreach标签

对于一些 SQL 语句中含有 in 条件,需要迭代条件集合来生成的情况,可以使用 foreach 来实现 SQL 条件的迭代。

Mybatisforeach 标签用于循环语句,它很好的支持了数据和 Listset 接口的集合,并对此提供遍历的功能。语法格式如下。

<foreach item="item" index="index" collection="list|array|map key" open="(" separator="," close=")">
    参数值
</foreach>

foreach 标签主要有以下属性,说明如下。

  • item:表示集合中每一个元素进行迭代时的别名。
  • index:指定一个名字,表示在迭代过程中每次迭代到的位置。
  • open:表示该语句以什么开始(既然是in条件语句,所以必然以(开始)。
  • separator:表示在每次进行迭代之间以什么符号作为分隔符(既然是 in 条件语句,所以必然以,作为分隔符)。
  • close:表示该语句以什么结束(既然是 in 条件语句,所以必然以)结束)。

使用foreach标签时,最关键、最容易出错的是 collection 属性,该属性是必选的,但在不同情况下该属性的值是不一样的,主要有以下 3 种情况:

  • 如果传入的是单参数且参数类型是一个 Listcollection 属性值为 list
  • 如果传入的是单参数且参数类型是一个 array 数组,collection 的属性值为 array
  • 如果传入的参数是多个,需要把它们封装成一个 Map,当然单参数也可以封装成 MapMap key是参数名,collection 属性值是传入的 List array 对象在自己封装的 Map 中的key

示例

需求:我们需要查询 user 表中 id 分别为1,2,3的用户

sql语句:select * from user where id=1 or id=2 or id=3

     select * from user where id in (1,2,3)

当前数据如下

idusernamesexbirthdayaddress
1张三2000-10-10四川成都
2李四2000-10-10四川成都
3王五2000-10-10四川成都

UserMapper.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="org.mybatis.example.UserMapper">
    <!-- 一对多 根据id查询用户及其关联的订单信息:级联查询的第一种方法(分步查询) -->
    <resultMap type="org.mybatis.example.User" id="myResult">
        <id property="id" column="id" />
        <result property="username" column="username" />
        <result property="sex" column="sex" />
        <result property="birthday" column="birthday" />
        <result property="address" column="address" />
    </resultMap>
    <select   parameterType="List" resultMap="myResult">
        select * from user
        <where>
            <!--
                collection:指定输入对象中的集合属性
                item:每次遍历生成的对象
                open:开始遍历时的拼接字符串
                close:结束时拼接的字符串
                separator:遍历对象之间需要拼接的字符串
                select * from user where 1=1 and id in (1,2,3)
              -->
            <foreach collection="ids" item="id" open="and id in (" close=") " separator=",">
                #{id}
            </foreach>
        </where>
    </select>
</mapper>

UserMapper 接口

package org.mybatis.example;

import org.apache.ibatis.annotations.Param;

import java.util.List;

public interface UserMapper {
    List<User> selectUserByListId(@Param("ids") List<Integer> ids);
}

测试

package org.mybatis.example;

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;
import java.util.ArrayList;
import java.util.List;

public class MainApplication {
    public static void main(String[] args) throws IOException {
        InputStream config = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(config);
        SqlSession ss = ssf.openSession();
        List<Integer> ids = new ArrayList<>();
        ids.add(1);
        ids.add(2);
        List<User> orderList = ss.getMapper(UserMapper.class).selectUserByListId(ids);
        for (User or : orderList) {
            System.out.println(or);
        }
    }
}

运行如下

==>  Preparing: select * from user WHERE id in ( ? , ? )
==> Parameters: 1(Integer), 2(Integer)
<==    Columns: id, username, sex, birthday, address
<==        Row: 1, 张三, 男, 2000-10-10, 四川成都
<==        Row: 2, 李四, 男, 2000-10-10, 四川成都
<==      Total: 2
User(id=1, username=张三, sex=男, birthday=2000-10-10, address=四川成都)
User(id=2, username=李四, sex=男, birthday=2000-10-10, address=四川成都)

Process finished with exit code 0

生成的sql语句为select * from user WHERE id in ( ? , ? )

bind标签

每个数据库的拼接函数或连接符号都不同,例如 MySQLconcat 函数、Oracle 的连接符号||等。这样 SQL 映射文件就需要根据不同的数据库提供不同的实现,显然比较麻烦,且不利于代码的移植。幸运的是,MyBatis 提供了 bind 标签来解决这一问题。

bind 标签可以通过 OGNL 表达式自定义一个上下文变量。

<select   resultMap="myResult">
        <bind name="username" value="'%'+_parameter+'%'" />
        select * from user where username like #{username}
    </select>

bind 元素属性如下。

  • value:对应传入实体类的某个字段,可以进行字符串拼接等特殊处理。
  • name:给对应参数取的别名。

以上代码中的“_parameter”代表传递进来的参数,它和通配符连接后,赋给了 pattern,然后就可以在 select 语句中使用这个变量进行模糊查询,不管是 MySQL 数据库还是 Oracle 数据库都可以使用这样的语句,提高了可移植性。

trim标签

MyBatis 中除了使用 if+where 实现多条件查询,还有一个更为灵活的元素 trim 能够替代之前的做法。

trim 一般用于去除 SQL 语句中多余的 AND 关键字、逗号或者给 SQL 语句前拼接 whereset 等后缀,可用于选择性插入、更新、删除或者条件查询等操作。trim 语法格式如下。

<trim prefix="前缀" suffix="后缀" prefixOverrides="忽略前缀字符" suffixOverrides="忽略后缀字符">
    SQL语句
</trim>

trim 中属性说明如下。

属性描述
prefix给SQL语句拼接的前缀,为 trim 包含的内容加上前缀
suffix给SQL语句拼接的后缀,为 trim 包含的内容加上后缀
prefixOverrides去除 SQL 语句前面的关键字或字符,该关键字或者字符由 prefixOverrides 属性指定。
suffixOverrides去除 SQL 语句后面的关键字或者字符,该关键字或者字符由 suffixOverrides 属性指定。

 ①、用 trim 改写 where 标签

<select   resultMap="myResult" parameterType="org.mybatis.example.User">
        select * from user
        <trim prefix="where" prefixOverrides="and | or">
            <if test="username != null">
               and username=#{username}
            </if>
            <if test="sex != null">
               and sex=#{sex}
            </if>
        </trim>
    </select>

 ②、用 trim 改写的 set 标签

<!-- 根据 id 更新 user 表的数据 -->
    <update   parameterType="org.mybatis.example.User">
        update user u
            <trim prefix="set" suffixOverrides=",">
                <if test="username != null and username != ''">
                    u.username = #{username},
                </if>
                <if test="sex != null and sex != ''">
                    u.sex = #{sex},
                </if>
            </trim>
         
         where id=#{id}
    </update>

免责声明:文章转载自《MyBatis(五)MyBatis动态SQL》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇vue与element ui搭配,关于eltable表格的排序问题模具行业的三坐标使用知识下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

SpringCloud实战十三:Gateway之 Spring Cloud Gateway 动态路由

前面分别对 Spring Cloud Zuul 与 Spring Cloud Gateway 进行了简单的说明,它门是API网关,API网关负责服务请求路由、组合及协议转换,客户端的所有请求都首先经过API网关,然后由它将匹配的请求路由到合适的微服务,是系统流量的入口,在实际生产环境中为了保证高可靠和高可用,尽量避免重启,如果有新的服务要上线时,可以通过...

lib和dll文件的初了解

lib,dll这两样东西在许多编程书中都很少出现,但实际工程中,这两样东西的作用确实非常重要,我觉得c++程序员都有必要了解这两样东西。 首先总共有 动态链接 和 静态链接 这两种链接方式 |静态链接:静态链接使用静态链接库lib,且只在源代码编译时用到(编译期)。编译生成静态库时会生成一个.lib文件.lib里面装载了各种类,函数的实现。这种静态链接的...

为知笔记+Typora+PicGo发表博客园博客

为知笔记+Typora+PicGo发表博客园博客 背景: 印象笔记编辑功能太弱,价格太坑,寻找替代品为“为知笔记”,一个主要原因“为知笔记”支持导入印象笔记格式笔记; Typora编写Markdown体验特别好,Markdown不足之处在于图片插入,这里使用“PicGo”上传SMMS图床解决; 环境:Windows 软件准备: 为知笔记 Typo...

用近似静态语言、强类型语言的TypeScript开发属于动态语言、弱类型语言的JavaScript

    对于我们写习惯了强类型、静态类型语言的开发人员来讲,开发弱类型、动态类型语言的程序可真是头痛呀。特别是的走微软技术路线,用习惯了强大无比的VS系列工具的开发人员,VS2003,VS2005,VS2008,VS2010,VS2012。。。。。。还有这些工具与其相结合的强类型语言,比如C#,那用起来多爽呀。     先来看看弱类型语言有些特点吧,如果自...

博文与文档发布玩法:Github + MWeb + 语雀 + Cnbolgs

本文会说两个话题, 1,如何将 Github 上的文档(如:dotnet-campus/doraemon-pocket: 哆啦A梦的口袋 - 收集效率工具与站点)发布到语雀。 2,如何在本地使用 Markdown 编辑博客,再推送到博客园。在我使用的方案中,这两者都使用到了 MWeb 这个 Markdown 编辑器,所以就放在一起啦。这篇博客就是在本地编辑...

DDD:策略模式如何结合动态表达式

企业应用中我们经常会遇到各种业务规则,针对这种规则,我们多数情况会采用策略模式,每种策略对应一个具体类和一个具体的配置界面。但是企业业务的规则经常变化,现有的策略随着时间的推移而不能满足要求,针对这种情况我们可以用动态表达式来解决。 动态表达式:在静态语言中动态的执行代码,目前可选的技术有:动态编译、Iron、Roslyn、内嵌小语言。 今天来测试一下内嵌...