Mybatis动态union all通过trim优化&trim标签详解

概述

原始sql

<select id="listUsersInfoIds" resultType="io.github.syske.user.UserInfo">
        select ui.id,
        ui.userId,
        ui.name,
        ui.active
        from (
        <if test="userIds != null and userIds.size > 0">
            select
            u.id,
            u.user_id as userId,
            u.name,
            u.active
            from user u
            where u.id in
            <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
                #{userId, jdbcType=BIGINT}
            </foreach>
            and u.active = true
        </if>
        <if test="postIds != null and postIds.size > 0">
            <if test="userIds != null and and userIds.size > 0">
                union all
            </if>
            select
            u.id,
            u.user_id as userId,
            u.name,
            u.active
            from post_user_mapping m,
            user u
            where m.post_id in
            <foreach collection="postIds" item="postId" open="(" close=")" separator=",">
                #{postId, jdbcType=BIGINT}
            </foreach>
            and m.user_id=u.id
            and u.active = true
        </if>
        <if test="groupIds != null and groupIds.size > 0">
			 <if test="(postIds != null and postIds.size > 0) and (userIds == null or userIds.size == 0)">
			     union all
			</if>
            select
            u.id,
            u.user_id as userId,
            u.name,
            u.active
            from group_user_mapping m,
            user u
            where m.group_id in
            <foreach collection="groupIds" item="groupId" open="(" close=")" separator=",">
                #{groupId, jdbcType=BIGINT}
            </foreach>
            and m.user_id=u.id
            and u.active = true
        </if>
        ) ui group by ui.id
    </select>

我觉得这种方式不够优雅,而且不够灵活,特别是如果我后面还需要加入union all语句的时候,那就要再多判断一个字段,越往后需要判断的字段就越多,然后我再网上找了一圈并没有找到解决方法,最后我打算看下mybatis的文档,幸运的是我还真找到了自己想要的答案。

解决方案

使用trim标签。
优化后的sql。

    <select id="listUsersInfoIds" resultType="net.coolcollege.user.facade.model.user.UserInfo">
        select ui.id,
        ui.userId,
        ui.name,
        ui.active
        from (
        <trim suffixOverrides="union all">
            <trim suffix="union all">
                <if test="userIds != null and userIds.size > 0">
                    select
                    u.id,
                    u.user_id as userId,
                    u.name,
                    u.active
                    from user u
                    where u.id in
                    <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
                        #{userId, jdbcType=BIGINT}
                    </foreach>
                    and u.active = true
                </if>
            </trim>
            <trim suffix="union all">
                <if test="postIds != null and postIds.size > 0">
                    select
                    u.id,
                    u.user_id as userId,
                    u.name,
                    u.active
                    from post_user_mapping m,
                    user u
                    where m.post_id in
                    <foreach collection="postIds" item="postId" open="(" close=")" separator=",">
                        #{postId, jdbcType=BIGINT}
                    </foreach>
                    and m.user_id=u.id
                    and u.active = true
                </if>
            </trim>
            <if test="groupIds != null and groupIds.size > 0">
                select
                u.id,
                u.user_id as userId,
                u.name,
                u.active
                from group_user_mapping m,
                user u
                where m.group_id in
                <foreach collection="groupIds" item="groupId" open="(" close=")" separator=",">
                    #{groupId, jdbcType=BIGINT}
                </foreach>
                and m.user_id=u.id
                and u.active = true
            </if>
        </trim>
        ) ui group by ui.id
    </select>

首先我通过一个大的trim包装所有子查询(之前通过union all连接),条件是移除最后的union all,然后再用一个trim标签包装除最后一个子查询之外的其他子查询,条件是在语句末尾加上union all,这样前面需要通过复杂if判断的语句就直接省略了,而且好处也很明显:
后续不论我增加多少个子查询,我只需要给子查询加上trim标签即可(条件都一样),而不需要关心其他子查询是否为空,这样整个sql不仅更简洁,而且扩展性也很强,后期不论我增加多少个子查询,只需要给子查询加上trim标签即可,而不需要处理其他复杂判断。

trim标签详解

在我们大多数需求场景下,mybatis提供的动态语句语法已经可以胜任了,比如if、where、choose、when、otherwise、foreach,再复杂一点的还有set,但是对于复杂需求他们都没办法完美解决(毕竟用if太过繁琐),于是发现了一个灵活性更高的标签——trim。

简单介绍

trim标签的作用就是帮助我们生成更复杂的sql,关于它的具体作用官方文档并没有给出明确说明,但是根据它的几个参数以及示例,我们可以看出它的用法。我们先看下trim标签的几个属性:

  • suffixOverrides:要替换的后缀(被替换的内容),去除sql语句后面的关键字或者字符,该关键字或者字符由suffixOverrides属性指定,同prefixOverrides。
  • suffix:给sql语句拼接的后缀,表示在trim包裹的SQL末尾添加指定内容(trim包裹的内容为空不添加)。
  • prefixOverrides:要替换的前缀(被替换的内容),去除sql语句前面的关键字或者字符,该关键字或者字符由prefixOverrides属性指定,假设该属性指定为"AND",当sql语句的开头为"AND",trim标签将会去除该"AND"。
  • prefix:给sql语句拼接的前缀,表示在trim包裹的SQL语句前面添加的指定内容(trim包裹的内容为空不添加)。

如果trim标签内的sql语句不存在,不会插入前缀或者后缀,插入前、后缀的前提是trim标签内有sql。

前置用法

先看第一个,也是官方给出的示例——通过trim来实现where标签,用where标签我们通常是这么写的:

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG
  <where>
    <if test="state != null">
         state = #{state}
    </if>
    <if test="title != null">
        AND title like #{title}
    </if>
    <if test="author != null and author.name != null">
        OR author_name like #{author.name}
    </if>
  </where>
</select>

用trim实现的话,可以这样写:

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG
 <trim prefix="where" prefixOverrides="AND | OR">
    <if test="state != null">
         state = #{state}
    </if>
    <if test="title != null">
        AND title like #{title}
    </if>
    <if test="author != null and author.name != null">
        OR author_name like #{author.name}
    </if>
  </trim>
</select>

后置用法

上面我们演示了前置替换的用法,下面我们来看下后置用法,后置用法是通过trim来实现set标签。

<update id="updateAuthorIfNecessary">
  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}
</update>

set标签的作用就是当如上语句中,第四个更新语句为空的时候,会将set标签内末尾的,移除掉,并在标签内语句开始处加上set关键字。用trim标签的话,可以这么写:

<update id="updateAuthorIfNecessary">
  update Author
    <trim prefix="set" suffixOverrides=','>
      <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>
    </trim>
  where id=#{id}
</update>

总结

关于trim我们就演示这么多,下面我们做一个简单总结:
prefix:表示前置要插入的内容,比如where、set,它可以单独使用;
suffix:表示后置插入的内容(同prefix);
prefixOverrides:表示前置要移除的内容(中文翻译前置覆写);
suffixOverrides:表示后置要移除的内容(同prefixOverrides);
也就是说trim本质上就是通过这四个属性,实现在语句前后加上或者移除相关内容,来实现复杂的动态sql,在实现方面也很简单,但是灵活度更多。

参考

记一次mybatis复杂动态sql拼接优化方案

  • 2
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
Mybatis-plus是MyBatis增强工具包,用于简化CRUD操作。该工具包为MyBatis提供了一些高效,有用,即用的功能,使用它可以有效地节省您的开发时间。 Mybatis-plus特征: 与MyBatis完全兼容 启动时自动配置 开箱即用的用于操作数据库的界面 强大而灵活的条件包装器 生成主键的多种策略 Lambda样式的API 全能和高度可定制的代码生成器 自动分页操作 SQL注入防御 支持活动记录 支持可插拔的自定义界面 内置许多有用的扩展 Mybatis-plus功能: 1、单表CURD(简单 + 批量)操作,自动完成(支持 like 比较等查询)。 2、分页插件,Count查询自动或自定义SQL查询。 3、Spring根据不同环境加载不同配置支持(支持typeAliasesPackage通配符扫描)。 【自动生成Entity  Mapper  Service文件】 Mybatis-plus更新日志: v3.4.3 增加瀚高数据库支持 增加注解 Order By 支持默认排序 Wrapper exists notExists orderBy groupBy 支持参数绑定 Wrapper 支持 setParamAlias 其它优化 优化 KeyGenerator 支持多实现多数据源注入 增强 ServiceImpl 泛型推断,解决多继承与代理问题 新增 PageDto 用于微服务对象传输序列化 新增 Page 提供静态 of 构造方式 增加代理 MethodHandleProxies 对 lambda 调试支持 调整 ActiveRecord 日志对象初始化 调整 ActiveRecord 模式 Model 类开发 pkVal 方法外部可用 删除标记过时代码 优化枚举值获取方式 分页 count 安全处理 Sequence 方法支持重写支持 升级 Mybatis 3.5.7 修复自动配置 lazy-initialization 无属性提示 修复 mysql on duplicate key update 字段名判断为表名问题 修复 lambda 条件 npe 异常 重构 lambda 信息提取方法 获取 lambda 信息不在序列化 合并 gitee pulls/ 141 fixed github issues/3208 3016 fixed github issues/3482 数据权限处理器支持 union all 调整事务未启用打印提示信息 单元测试优化相关依赖升级

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

融极

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值