【Mybatis】13 动态SQL

还是先准备演示环境

数据库:

CREATE TABLE `t_user` (
  `id` int NOT NULL AUTO_INCREMENT,
  `last_name` varchar(10) DEFAULT NULL,
  `gender` int DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

【数据随便加,注意写一个同名不同性的】

实体类

package cn.dai.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.ibatis.type.Alias;

/**
 * @author ArkD42
 * @file Mybatis
 * @create 2020 - 05 - 30 - 20:35
 */

@Alias("user")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {

    private Integer id;
    private String last_name;
    private Boolean gender;
}

映射接口

package cn.dai.mapper;

import cn.dai.pojo.User;

import java.util.List;

/**
 * @author ArkD42
 * @file Mybatis
 * @create 2020 - 05 - 30 - 20:36
 */
public interface UserMapper {

    List<User> getUsersByNameAndGender(User user);
}

映射器

<?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接口名称-->
<mapper namespace="cn.dai.mapper.UserMapper">

    <select id="getUsersByNameAndGender" parameterType="user" resultType="user">
        SELECT
            *
        FROM
            t_user
        WHERE
            last_name = #{last_name}
            and gender = #{gender}
    </select>
</mapper>

测试

import cn.dai.mapper.UserMapper;
import cn.dai.pojo.User;
import cn.dai.utils.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

/**
 * @author ArkD42
 * @file Mybatis
 * @create 2020 - 05 - 30 - 21:17
 */
public class BuildTest {

    @Test
    public void queryUsersByNameAndGender(){
        SqlSession sqlSession = MybatisUtil.getSqlSession(true);
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        List<User> users = userMapper.getUsersByNameAndGender(new User(null, "阿伟", 1));

        for (User user: users) {
            System.out.println(user);
        }

        sqlSession.close();
    }
}

结果

1、IF 语句

默认情况性别的状态码就是1和0

但是不排除乱写的情况,比如注入其他的数字

这时候可以使用动态SQL帮助我们进行一个逻辑控制

如果注入了不符号我们期望的数值,那么将设置为默认值即可

<?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接口名称-->
<mapper namespace="cn.dai.mapper.UserMapper">

    <select id="getUsersByNameAndGender" parameterType="user" resultType="user">
        SELECT
            *
        FROM
            t_user
        WHERE
            last_name = #{last_name}
        <if test="gender==0 or gender==1">
            and gender = #{gender}
        </if>
        
    </select>
</mapper>

似乎mybatis这样查询的会是0&1的结果,如果我们希望只查询0,那么就不要写1即可

另外两个条件可以使用IF

    <select id="getUsersByNameAndGender" parameterType="user" resultType="user">
        SELECT
            *
        FROM
            t_user
        WHERE
        <if test="last_name != null">
            last_name = #{last_name}
        </if>
            
        <if test="gender==0 or gender==1">
            and gender = #{gender}
        </if>

    </select>

2、WHERE 语句

如果出现的元数据问题不是我们的性别状态,而是前面的姓名问题

姓名注入了空值,那么程序将会异常中断

直接打印这样的错误信息

org.apache.ibatis.exceptions.PersistenceException: 
### Error querying database.  Cause: java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 5
### The error may exist in mapper/UserMapper.xml
### The error may involve cn.dai.mapper.UserMapper.getUsersByNameAndGender-Inline
### The error occurred while setting parameters
### SQL: SELECT             *         FROM             t_user         WHERE
### Cause: java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 5

其实道理也很简单

我们为了避免这样的SQL语法错误,提供了更好的动态SQL标签:使用Where

<?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接口名称-->
<mapper namespace="cn.dai.mapper.UserMapper">

    <select id="getUsersByNameAndGender" parameterType="user" resultType="user">
        SELECT
            *
        FROM
            t_user
        <where>
            <if test="last_name != null">
                last_name = #{last_name}
            </if>

            <if test="gender==0 or gender==1">
                and gender = #{gender}
            </if>
        </where>
    </select>
    
</mapper>

查询结果:

当姓名为空,就按照下面的性别条件查询

不再有异常问题

这是因为WHERE标签,可以动态的实现AND或者OR条件连接

但是不要删除了条件连接,这样还是会报语法错误

所以WHERE只是简单的判断和补充

3、TRIM 语句

可以动态的添加删除内容

- prefix 前置添加

- suffix 后置添加

- suffixOverrides 前置删除

- prefixOverrides 后置删除

    <select id="getUsersByNameAndGender" parameterType="user" resultType="user">
        SELECT
            *
        FROM
            t_user
        <trim suffixOverrides="and" prefix="where">
            <if test="last_name != null">
                last_name = #{last_name} and
            </if>

            <if test="gender==0 or gender==1">
                gender = #{gender}
            </if>
        </trim>
    </select>

不是很能明白前缀和后缀

4、CHOOSE(WHEN & OTHERS) 语句

可以执行多个条件判断,问题是只能有一个条件适用

【根据上面的结果能推断的适用条件是搭配其他的前置条件,并成为最后一个多选一的关键条件】

就纯语法案例:

- 有名字,按名字查

- 没有按性别查

- 再没有自己写一个条件查

    <select id="getUsersByNameAndGender" parameterType="user" resultType="user">
        SELECT
            *
        FROM
            t_user
        WHERE
        <choose>
            <when test="last_name != null">
                last_name = #{last_name}
            </when>

            <when test="gender == 1 or gender == 0">
                gender = #{gender}
            </when>

            <otherwise>
                id between 1 and 10
            </otherwise>
        </choose>
    </select>

测试一个上述条件都没有最后执行otherwise的结果

[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Opening JDBC Connection
[org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 11939193.
[cn.dai.mapper.UserMapper.getUsersByNameAndGender]-==>  Preparing: SELECT * FROM t_user WHERE gender = ? 
[cn.dai.mapper.UserMapper.getUsersByNameAndGender]-==> Parameters: 0(Integer)
[cn.dai.mapper.UserMapper.getUsersByNameAndGender]-<==      Total: 4
User(id=1, last_name=阿伟, gender=0)
User(id=3, last_name=杰哥, gender=0)
User(id=4, last_name=阿强, gender=0)
User(id=5, last_name=虞姬, gender=0)
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@b62d79]
[org.apache.ibatis.datasource.pooled.PooledDataSource]-Returned connection 11939193 to pool.

Process finished with exit code 0

5、SET 语句

用于删除更新多个字段的逗号

    <update id="updateUserById" parameterType="int" >
        UPDATE 
            t_user
        <set>
            <if test="last_name != null">
                last_name = #{last_name},
            </if>

            <if test="gender == 1 or gender == 0">
                gender = #{gender}
            </if>
        </set>
        WHERE 
            id = #{id}
    </update>

动态的删除逗号分隔

6、FOREACH语句

在之前的一个动态SQL标签中

我们这样进行筛选一个连续的部分,或者说是使用in语句也可以

Mybatis提供一个FOREACH标签来完成这个功能

List<User> getUserByFragment(List<Integer> id);

查看约束我们发现这个遍历标签只允许集合类型

映射器配置

    <select id="getUsersByFragment" parameterType="int" resultType="user" >
        SELECT *
        FROM t_user
        <where>
            id in
            <foreach 
                    collection="id"
                    item="el"
                    index="(" 
                    separator=","
                    close=")"
            >
                #{el}
            </foreach>
        </where>

    </select>

截图说明:

但是这样的结果更像是拼接出来的一样,估计底层就是拼接吧。。。

上面的这些都不再做演示了

Script标签

使用注解SQL实现时,可以使用script标签完成动态SQL

比如:

@Update({"<script>",
      "update Author",
      "  <set>",
      "    <if test='username != null'>username=#{username},</if>",
      "    <if test='password != null'>password=#{password},</if>",
      "    <if test='email != null'>email=#{email},</if>",
      "    <if test='bio != null'>bio=#{bio}</if>",
      "  </set>",
      "where id=#{id}",
      "</script>"})
    void updateAuthorValues(Author author);

SQL片段

这个元素可以用来定义可重用的 SQL 代码片段,以便在其它语句中使用。

参数可以静态地(在加载的时候)确定下来,并且可以在不同的 include 元素中定义不同的参数值。

比如:

<sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password </sql>

这个 SQL 片段可以在其它语句中使用,例如:

<select id="selectUsers" resultType="map">
  select
    <include refid="userColumns"><property name="alias" value="t1"/></include>,
    <include refid="userColumns"><property name="alias" value="t2"/></include>
  from some_table t1
    cross join some_table t2
</select>

也可以在 include 元素的 refid 属性或内部语句中使用属性值,例如:

<sql id="sometable">
  ${prefix}Table
</sql>

<sql id="someinclude">
  from
    <include refid="${include_target}"/>
</sql>

<select id="select" resultType="map">
  select
    field1, field2, field3
  <include refid="someinclude">
    <property name="prefix" value="Some"/>
    <property name="include_target" value="sometable"/>
  </include>
</select>
原文地址:https://www.cnblogs.com/mindzone/p/12993193.html