java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > mybatis if/trim/where/set标签

MyBatis动态if/trim/where/set标签实例详解

作者:jjjava2.0

MyBatis是一个支持普通SQL 查询、存储过程和高级映射的持久层框架,它消除了几乎所有的JDBC 代码和参数的手工设置以及结果集的检索,本文给大家介绍MyBatis动态if/trim/where/set标签,感兴趣的朋友跟随小编一起看看吧

动态SQL是Mybatis的强大特性之一,能够完成不同的sql拼接

官网文档为:

本节是对上一章内容经行的扩充

一、<if>标签

在注册用户时,我们经常会遇到必填字段和非必填字段的情况。例如,性别(gender)可能是一个非必填字段,程序需要根据用户是否填写该字段来动态生成SQL语句。这时就可以使用MyBatis的<if>标签来实现条件判断。

1.1 接口定义

// 根据条件插入用户信息
Integer insertUserByCondition(UserInfo userInfo);

1.2 Mapper.xml实现

<insert id="insertUserByCondition">
    INSERT INTO userinfo (
        username,
        `password`,
        age,
        <if test="gender != null">
            gender,
        </if>
        phone
    ) VALUES (
        #{username},
        #{password},
        #{age},
        <if test="gender != null">
            #{gender},
        </if>
        #{phone}
    )
</insert>

1.3 注解方式实现(不推荐)

@Insert("<script>" +
        "INSERT INTO userinfo (username, `password`, age, " +
        "<if test='gender!=null'>gender,</if> " +
        "phone) " +
        "VALUES(#{username}, #{password}, #{age}, " +
        "<if test='gender!=null'>#{gender},</if> " +
        "#{phone})" +
        "</script>")
Integer insertUserByCondition(UserInfo userInfo);

注意事项:

  1. test属性test="gender != null"中的gender是传入Java对象的属性名,不是数据库字段名。
  2. 字段默认值问题:不能简单地将字段设置为null,因为如果数据库字段有默认值,插入null会使用默认值而不是保持为空。
  3. 注解方式:需要使用<script></script>标签包裹动态SQL,但IDEA不会进行格式检查,容易出错,建议初学者使用XML方式。

二、<trim>标签

当有多个字段都可能成为选填项时,使用多个<if>标签会导致SQL语句末尾出现多余的逗号。这时可以使用<trim>标签结合<if>标签,对多个字段进行动态生成。

2.1 <trim>标签属性

2.2 Mapper.xml实现

<insert id="insertUserByCondition">
    INSERT INTO userinfo
    <trim prefix="(" suffix=")" suffixOverrides=",">
        <if test="username != null">
            username,
        </if>
        <if test="password != null">
            `password`,
        </if>
        <if test="age != null">
            age,
        </if>
        <if test="gender != null">
            gender,
        </if>
        <if test="phone != null">
            phone,
        </if>
    </trim>
    VALUES
    <trim prefix="(" suffix=")" suffixOverrides=",">
        <if test="username != null">
            #{username},
        </if>
        <if test="password != null">
            #{password},
        </if>
        <if test="age != null">
            #{age},
        </if>
        <if test="gender != null">
            #{gender},
        </if>
        <if test="phone != null">
            #{phone}
        </if>
    </trim>
</insert>

2.3 动态解析过程

在SQL动态解析时,第一个<trim>部分会进行如下处理:

  1. 基于prefix配置,开始部分加上(
  2. 基于suffix配置,结束部分加上)
  3. 多个<if>组织的语句都以,结尾,在最后拼接好的字符串还会以,结尾,会基于suffixOverrides配置去掉最后一个,

2.4 注解方式实现(不推荐)

@Insert("<script>" +
        "INSERT INTO userinfo " +
        "<trim prefix='(' suffix=')' suffixOverrides=','>" +
        "<if test='username!=null'>username,</if>" +
        "<if test='password!=null'>password,</if>" +
        "<if test='age!=null'>age,</if>" +
        "<if test='gender!=null'>gender,</if>" +
        "<if test='phone!=null'>phone,</if>" +
        "</trim> " +
        "VALUES " +
        "<trim prefix='(' suffix=')' suffixOverrides=','>" +
        "<if test='username!=null'>#{username},</if>" +
        "<if test='password!=null'>#{password},</if>" +
        "<if test='age!=null'>#{age},</if>" +
        "<if test='gender!=null'>#{gender},</if>" +
        "<if test='phone!=null'>#{phone}</if>" +
        "</trim>" +
        "</script>")
Integer insertUserByCondition(UserInfo userInfo);

三、<where>标签

在实际业务中,我们经常需要根据不同的筛选条件动态组装WHERE子句。<where>标签可以智能地处理这种情况,避免SQL语法错误。

3.1 应用场景

需求:传入用户对象,根据属性做WHERE条件查询,用户对象中属性不为null的,都作为查询条件。例如username为"a",则查询条件为WHERE username="a"。

3.2 接口定义

// 根据条件查询用户
List<UserInfo> queryByCondition(UserInfo userInfo);

3.3 Mapper.xml实现

<select id="queryByCondition" resultType="com.example.demo.model.UserInfo">
    SELECT id, username, age, gender, phone, delete_flag, create_time, update_time
    FROM userinfo
    <where>
        <if test="age != null">
            AND age = #{age}
        </if>
        <if test="gender != null">
            AND gender = #{gender}
        </if>
        <if test="deleteFlag != null">
            AND delete_flag = #{deleteFlag}
        </if>
    </where>
</select>

3.4 <where>标签特性

3.5 注解方式实现

@Select("<script>" +
        "SELECT id, username, age, gender, phone, delete_flag, create_time, update_time " +
        "FROM userinfo " +
        "<where>" +
        "    <if test='age != null'> AND age = #{age} </if>" +
        "    <if test='gender != null'> AND gender = #{gender} </if>" +
        "    <if test='deleteFlag != null'> AND delete_flag = #{deleteFlag} </if>" +
        "</where>" +
        "</script>")
List<UserInfo> queryByCondition(UserInfo userInfo);

四、<set>标签

在更新操作中,我们经常需要根据传入的对象属性来动态更新字段。<set>标签可以智能地处理UPDATE语句中的SET部分,避免多余的逗号问题。

4.1 应用场景

需求:根据传入的用户id属性,修改其他不为null的属性。只更新有值的字段,避免将null值更新到数据库。

4.2 接口定义

// 根据条件更新用户信息
Integer updateUserByCondition(UserInfo userInfo);

4.3 Mapper.xml实现

<update id="updateUserByCondition">
    UPDATE userinfo
    <set>
        <if test="username != null">
            username = #{username},
        </if>
        <if test="age != null">
            age = #{age},
        </if>
        <if test="deleteFlag != null">
            delete_flag = #{deleteFlag},
        </if>
    </set>
    WHERE id = #{id}
</update>

4.4 <set>标签特性

4.5 注解方式实现

@Update("<script>" +
        "UPDATE userinfo " +
        "<set>" +
        "    <if test='username!=null'>username=#{username},</if>" +
        "    <if test='age!=null'>age=#{age},</if>" +
        "    <if test='deleteFlag!=null'>delete_flag=#{deleteFlag},</if>" +
        "</set>" +
        "WHERE id=#{id}" +
        "</script>")
Integer updateUserByCondition(UserInfo userInfo);

4.6 使用注意事项

  1. 条件判断test属性中的字段名是Java对象的属性名
  2. 逗号处理:即使最后一个条件不满足,<set>标签也会自动处理末尾的逗号
  3. 性能考虑:只更新必要的字段,减少数据库的写操作

六、<include>标签

在XML映射文件中配置SQL时,经常会遇到很多重复的SQL片段,导致代码冗余。<include>标签结合<sql>标签可以解决这个问题,通过抽取可重用的SQL片段来提高代码的复用性和可维护性。

6.1 问题分析

在开发过程中,多个SQL语句可能包含相同的字段列表或条件片段,例如:

<!-- 查询所有用户 -->
<select id="queryAllUser" resultMap="BaseMap">
    SELECT id, username, age, gender, phone, delete_flag, create_time, update_time
    FROM userinfo
</select>
<!-- 根据ID查询用户 -->
<select id="queryById" resultType="com.example.demo.model.UserInfo">
    SELECT id, username, age, gender, phone, delete_flag, create_time, update_time
    FROM userinfo WHERE id = #{id}
</select>

可以看到,两个查询语句都包含了相同的字段列表,这违反了DRY(Don't Repeat Yourself)原则。

6.2 <sql>标签:定义可重用SQL片段

<sql>标签用于定义可重用的SQL片段:

<sql id="allColumn">
    id, username, age, gender, phone, delete_flag, create_time, update_time
</sql>

特性说明:

6.3 <include>标签:引用SQL片段

<include>标签通过refid属性引用已定义的SQL片段:

<!-- 查询所有用户 -->
<select id="queryAllUser" resultMap="BaseMap">
    SELECT
    <include refid="allColumn"/>
    FROM userinfo
</select>
<!-- 根据ID查询用户 -->
<select id="queryById" resultType="com.example.demo.model.UserInfo">
    SELECT
    <include refid="allColumn"/>
    FROM userinfo WHERE id = #{id}
</select>

6.4 使用优势

  1. 代码复用:避免重复编写相同的SQL片段
  2. 维护方便:当字段列表需要修改时,只需修改一处
  3. 可读性高:SQL语句更加简洁清晰
  4. 减少错误:避免因复制粘贴导致的字段遗漏或错误

6.5 高级用法:带参数的SQL片段

<include>标签还可以结合<property>标签传递参数:

<!-- 定义带表名前缀的SQL片段 -->
<sql id="userColumns">
    ${alias}.id, ${alias}.username, ${alias}.age
</sql>
<!-- 使用带参数的SQL片段 -->
<select id="queryUserWithAlias" resultType="com.example.demo.model.UserInfo">
    SELECT
    <include refid="userColumns">
        <property name="alias" value="u"/>
    </include>
    FROM userinfo u
</select>

6.6 最佳实践建议

6.7 实际应用场景

场景一:多表查询字段复用

<sql id="userBaseColumns">
    u.id, u.username, u.email, u.create_time
</sql>
<sql id="orderBaseColumns">
    o.id as order_id, o.order_no, o.amount, o.status
</sql>
<select id="queryUserWithOrders" resultMap="UserOrderMap">
    SELECT
    <include refid="userBaseColumns"/>,
    <include refid="orderBaseColumns"/>
    FROM user u
    LEFT JOIN orders o ON u.id = o.user_id
    WHERE u.id = #{userId}
</select>

场景二:复杂条件复用

<sql id="activeUserCondition">
    delete_flag = 0 AND status = 1
</sql>
<select id="queryActiveUsers" resultType="com.example.demo.model.UserInfo">
    SELECT * FROM userinfo
    WHERE <include refid="activeUserCondition"/>
</select>
<select id="countActiveUsers" resultType="java.lang.Integer">
    SELECT COUNT(*) FROM userinfo
    WHERE <include refid="activeUserCondition"/>
</select>

七、小结

到此这篇关于MyBatis动态if/trim/where/set标签详解的文章就介绍到这了,更多相关mybatis if/trim/where/set标签内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文