欢迎光临瑞安九九信息网
详情描述
XML重复查询一条Sql语句的解决方法

1. 使用SQL片段(SQL Fragments)

MyBatis示例:

<!-- 定义可重用的SQL片段 -->
<sql id="baseColumns">
    id, name, email, create_time
</sql>

<sql id="userWhereCondition">
    WHERE status = 'ACTIVE'
    <if test="name != null">
        AND name LIKE CONCAT('%', #{name}, '%')
    </if>
</sql>

<!-- 引用SQL片段 -->
<select id="selectUsers" resultType="User">
    SELECT 
    <include refid="baseColumns"/>
    FROM users
    <include refid="userWhereCondition"/>
</select>

<select id="selectUserCount" resultType="int">
    SELECT COUNT(*)
    FROM users
    <include refid="userWhereCondition"/>
</select>

2. 使用动态SQL构建通用查询

<sql id="dynamicWhere">
    <where>
        <if test="id != null">AND id = #{id}</if>
        <if test="name != null">AND name = #{name}</if>
        <if test="status != null">AND status = #{status}</if>
        <if test="startDate != null">AND create_time >= #{startDate}</if>
        <if test="endDate != null">AND create_time <= #{endDate}</if>
    </where>
</sql>

<!-- 多个查询复用同一个WHERE条件 -->
<select id="findByCondition" resultType="User">
    SELECT * FROM users
    <include refid="dynamicWhere"/>
    ORDER BY create_time DESC
</select>

<select id="countByCondition" resultType="int">
    SELECT COUNT(*) FROM users
    <include refid="dynamicWhere"/>
</select>

3. 使用继承/模板模式

<!-- 基础查询模板 -->
<sql id="baseSelect">
    SELECT 
        u.id,
        u.username,
        u.email,
        d.department_name
    FROM users u
    LEFT JOIN department d ON u.dept_id = d.id
</sql>

<!-- 具体查询扩展基础模板 -->
<select id="getUserList" resultType="UserDTO">
    <include refid="baseSelect"/>
    WHERE u.status = 'ACTIVE'
    ORDER BY u.create_time DESC
</select>

<select id="getUserById" resultType="UserDTO">
    <include refid="baseSelect"/>
    WHERE u.id = #{id}
</select>

4. 使用参数化排序和分页

<sql id="pagination">
    <if test="orderBy != null">
        ORDER BY ${orderBy}
        <if test="orderDirection != null">
            ${orderDirection}
        </if>
    </if>
    <if test="offset != null and limit != null">
        LIMIT #{offset}, #{limit}
    </if>
</sql>

<select id="findWithPagination" resultType="User">
    SELECT * FROM users
    WHERE status = 'ACTIVE'
    <include refid="pagination"/>
</select>

5. 创建通用查询构建器(高级)

<!-- 定义查询组件 -->
<sql id="commonConditions">
    <trim prefix="WHERE" prefixOverrides="AND |OR ">
        <if test="filters != null">
            <foreach collection="filters" item="filter" separator=" AND ">
                ${filter.column} ${filter.operator} #{filter.value}
            </foreach>
        </if>
    </trim>
</sql>

<select id="dynamicQuery" resultType="map">
    SELECT ${columns} FROM ${tableName}
    <include refid="commonConditions"/>
</select>

6. 最佳实践建议

优点:

  • 减少代码重复,提高维护性
  • 统一修改,一处修改全局生效
  • 提高可读性,复杂的SQL逻辑被抽象

注意事项:

  • ⚠️ 不要过度抽象,保持SQL的可读性
  • ⚠️ 避免在片段中使用过于复杂的逻辑
  • ⚠️ 注意SQL片件的顺序依赖问题
  • ⚠️ 考虑数据库兼容性(如分页语法差异)

推荐结构:

<!-- 1. 列定义片段 -->
<sql id="baseColumns">...</sql>

<!-- 2. 关联表片段 -->
<sql id="baseJoins">...</sql>

<!-- 3. 通用条件片段 -->
<sql id="baseConditions">...</sql>

<!-- 4. 排序分页片段 -->
<sql id="baseOrderAndPage">...</sql>

<!-- 具体查询组合使用 -->
<select id="query" resultType="...">
    SELECT <include refid="baseColumns"/>
    FROM main_table
    <include refid="baseJoins"/>
    <include refid="baseConditions"/>
    <include refid="baseOrderAndPage"/>
</select>

7. 替代方案:注解方式

如果XML中仍然有大量重复,可以考虑:

// 使用MyBatis注解
@SelectProvider(type = UserSqlBuilder.class, method = "buildSelectSql")
List<User> selectUsers(UserQuery query);

// 构建通用SQL
public class UserSqlBuilder {
    public static String buildSelectSql(UserQuery query) {
        return new SQL()
            .SELECT("*")
            .FROM("users")
            .WHERE("status = 'ACTIVE'")
            .WHERE(buildConditions(query))
            .toString();
    }

    private static String buildConditions(UserQuery query) {
        // 动态构建条件
    }
}

选择哪种方法取决于:

SQL的复杂程度 团队的熟悉程度 项目的具体需求 维护的便利性

建议:对于中等复杂度的项目,使用SQL片段是最平衡的选择;对于复杂查询逻辑,可以考虑使用查询构建器模式。

相关推荐
在线学习平台提供的微证书和传统资格证书,在求职时认可度有何不同?
在线学习平台提供的微证书和传统资格证书,在求职时认可度有何不同?
房产证署名与不动产登记簿不一致时,以哪个为准确定产权?
房产证署名与不动产登记簿不一致时,以哪个为准确定产权?
指纹信息如果泄露,是否会比密码泄露带来更长期且无法挽回的安全风险?
指纹信息如果泄露,是否会比密码泄露带来更长期且无法挽回的安全风险?
节假日高速免费期间,如何利用导航软件实时规避拥堵路段和事故点?
节假日高速免费期间,如何利用导航软件实时规避拥堵路段和事故点?
使用vbs脚本来监控windows服务器上的应用程序(不存在就启动)
使用vbs脚本来监控windows服务器上的应用程序(不存在就启动)
当我们发现被默认勾选了某些付费项目时,第一时间应该采取的步骤有哪些?
当我们发现被默认勾选了某些付费项目时,第一时间应该采取的步骤有哪些?
对于零基础的转行者,选择考取哪个领域的入门证书成功率更高?
对于零基础的转行者,选择考取哪个领域的入门证书成功率更高?
不定时工作制下的员工,如果发生工作相关的伤害能否认定工伤?
不定时工作制下的员工,如果发生工作相关的伤害能否认定工伤?
小区门口或路边那块带奔跑小人图标的小牌子,是不是就在告诉你避难场所在哪?
小区门口或路边那块带奔跑小人图标的小牌子,是不是就在告诉你避难场所在哪?
对于不擅长使用智能设备的老年人,生物识别社保卡真的更方便了吗?
对于不擅长使用智能设备的老年人,生物识别社保卡真的更方便了吗?
当越来越多的人设置三天可见,这对我们维系线上人际关系产生了哪些影响?
当越来越多的人设置三天可见,这对我们维系线上人际关系产生了哪些影响?
在海外因交通事故等突发情况受伤,领事官员能够协助联系家属或医院吗?
在海外因交通事故等突发情况受伤,领事官员能够协助联系家属或医院吗?
购买新家电时,除了能效标识,还有哪些关键参数需要关注以实现省电?
购买新家电时,除了能效标识,还有哪些关键参数需要关注以实现省电?
社保卡揣在兜里好几年只用来买药,从没去银行开过那个
社保卡揣在兜里好几年只用来买药,从没去银行开过那个"隐藏功能",会有什么后果?
进口食品的有机食品认证标志和国产的有啥不同?怎么判断是不是等效认可?
进口食品的有机食品认证标志和国产的有啥不同?怎么判断是不是等效认可?
2026年,个人名下房产情况如何影响首套房与二套房的官方认定标准?
2026年,个人名下房产情况如何影响首套房与二套房的官方认定标准?