实现 Mybatis >、< 等多表联合的动态 sql 拼接

前端页面

前端页面的条件查询,通过点赞数或反对数,或者两者都有的条件进行一个多表查询(联合评论表和用户表):

image

操作符可以是:大于、大于等于、等于、小于、小于等于。

后端 Controller

我设置的是 Get 请求,由于这个请求被用到的地方很多,我设置了 6 个非必填的参数(其实建议 Post 请求,并通过 Map 接收数据)。

@GetMapping("/query")
public R<List<BuchComment>> query(@RequestParam(required = false) String buchType,
                                  @RequestParam(required = false) Integer buchId,
                                  @RequestParam(required = false) String diggOp,
                                  @RequestParam(required = false) String buryOp,
                                  @RequestParam(required = false) Integer digg,
                                  @RequestParam(required = false) Integer bury) {
    ParamsMap<String, Object> paramsMap = new ParamsMap<>();
    paramsMap.set("buchType", buchType)
            .set("buchId", buchId)
            .set("diggOp", diggOp)
            .set("buryOp", buryOp)
            .set("bury", bury)
            .set("digg", digg);
    return service.query(paramsMap.getMap());
}

这个请求函数写的有点啰嗦了,不过并无大碍。

后端 Mapper

SQL 分段1

这是联合查询的 SQL 分段,有可能多处用到这个 SQL,就复用起来。

<sql id="frg1">
    SELECT bc.*,
           u.username      u_username,
           u.level         u_level,
           u.id            u_id,
           u.profile_photo u_profile_photo
    FROM buch_comments bc
             INNER JOIN buchs b ON b.id = bc.buch_id
             INNER JOIN users u ON u.id = bc.user_id
</sql>

resultMap 标签

查询出来的结果是一个集合,其一个元素中的关系是一个评论对应一个用户。

<resultMap id="queryCommsResultMap" type="BuchComment" autoMapping="true">
    <id property="id" column="id"/>
    <association property="user" javaType="com.buchstadt.pojo.BuchComment$User" autoMapping="true"
                 columnPrefix="u_">
        <id property="id" column="id"/>
    </association>
</resultMap>

SQL 分段2

这个 SQL 分段是一个操作符的判断,选择到底使用哪一个 SQL 条件,由于这里被用到的两次,所以写到 SQL 分段中以复用。

<sql id="frg2">
    <choose>
        <!-- &gt; 大于-->
        <when test="${operName} == 'gt'">
            AND ${colFiled} &gt; #{${proFiled}}
        </when>
        <!-- &gt;= 大于等于-->
        <when test="${operName} == 'gteq'">
            AND ${colFiled} &gt;= #{${proFiled}}
        </when>
        <!-- &lt; 小于-->
        <when test="${operName} == 'lt'">
            AND ${colFiled} &lt; #{${proFiled}}
        </when>
        <!-- &lt;= 小于等于-->
        <when test="${operName} == 'lteq'">
            AND ${colFiled} &lt;= #{${proFiled}}
        </when>
        <!-- eq 等于-->
        <when test="${operName} == 'eq'">
            AND ${colFiled} = #{${proFiled}}
        </when>
    </choose>
</sql>

这个标签中有一个重要的注意事项,${} 是取出 property 标签传入的值,作为表中的可能存在的字段。而,#{} 是取出值,作为一个变量,如数字、字符串等,不是表中的字段。

我解释一下 #{${proFiled}} 的用法。因为 property 传递过来的值只能通过 ${} 获取,而又因可能将其作为一个 SQL 变量使用,所以外面包裹一个 #{}

select 查询标签

select 查询标签中最重要的是 if 标签了。首先通过 trim 标签去除多余的 AND 关键字,以及给第一个添加一个 WHERE 关键字。

接着就取出一个个 Map 中的元素,判断其值是否空或者 null 等。

<select id="query" resultMap="queryCommsResultMap">
    <include refid="frg1"/>
    <trim prefix="WHERE" prefixOverrides="AND">
        <if test="buchType != null and buchType != '' and buchType != 'all'">
            AND bc.type = #{buchType}
        </if>
        <if test="buchId != null and buchId != 0">
            AND bc.buch_id = #{buchId}
        </if>
        <if test="bury != null and operator != ''">
            <include refid="frg2">
                <property name="colFiled" value="bc.bury"/>
                <property name="operName" value="buryOp"/>
                <property name="proFiled" value="bury"/>
            </include>
        </if>
        <if test="digg != null and operator != ''">
            <include refid="frg2">
                <property name="colFiled" value="bc.digg"/>
                <property name="operName" value="diggOp"/>
                <property name="proFiled" value="digg"/>
            </include>
        </if>
    </trim>
</select>

这里有一个细节,bc.digg 意思是,选取多表查询中被重命名的表 bc,得到其字段 digg 。

posted @ 2023-06-19 01:58  Himmelbleu  阅读(18)  评论(0编辑  收藏  举报