You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为不同表与视图复用原生SQL查询的重复代码?

问题描述

我想了解是否可以实现以下场景的代码复用:现有两个Spring Data JPA原生查询,代码如下:

@Query(nativeQuery = true,
        value = "SELECT TA.* " +
                "FROM TABLE_A TA " +
                "WHERE " +
                "(LOWER(NAME) LIKE NVL(CONCAT('%', CONCAT(LOWER(:#{#filter.name}), '%')), NAME) " +
                "OR LOWER(CODE) LIKE NVL(CONCAT('%', CONCAT(LOWER(:#{#filter.code}), '%')), CODE)) " +
                "AND (TO_CHAR(USER_WORKFLOW_ID) IN (:#{#filter.userWorkFlowIds}) OR COALESCE(:#{#filter.userWorkFlowIds}, NULL) IS NULL) " +
                "ORDER BY " +
                "      CASE " +
                "         WHEN :#{#filter.orderByColumn.index} = 4 AND :#{#filter.orderDirection.index} = -1 THEN VERSION " +
                "      END DESC, " +
                "      CASE " +
                "         WHEN :#{#filter.orderByColumn.index} = 10 AND :#{#filter.orderDirection.index} = -1 THEN PROGRESS " +
                "      END DESC " +
                "OFFSET LOWER(:#{#filter.pageSize} * :#{#filter.pageIndex}) ROWS FETCH NEXT LOWER(:#{#filter.pageSize}) ROWS ONLY ")
List<TableAItems> findByFilter(@Param(value = "filter") TAFilter filter);

以及

@Query(nativeQuery = true,
        value = "SELECT TB.* " +
                "FROM TABLE_B TB " +
                "WHERE " +
                "(LOWER(NAME) LIKE NVL(CONCAT('%', CONCAT(LOWER(:#{#filter.name}), '%')), NAME) " +
                "OR LOWER(CODE) LIKE NVL(CONCAT('%', CONCAT(LOWER(:#{#filter.code}), '%')), CODE)) " +
                "AND (TO_CHAR(USER_WORKFLOW_ID) IN (:#{#filter.userWorkFlowIds}) OR COALESCE(:#{#filter.userWorkFlowIds}, NULL) IS NULL) " +
                "ORDER BY " +
                "      CASE " +
                "         WHEN :#{#filter.orderByColumn.index} = 4 AND :#{#filter.orderDirection.index} = -1 THEN VERSION " +
                "      END DESC, " +
                "      CASE " +
                "         WHEN :#{#filter.orderByColumn.index} = 10 AND :#{#filter.orderDirection.index} = -1 THEN PROGRESS " +
                "      END DESC " +
                "OFFSET LOWER(:#{#filter.pageSize} * :#{#filter.pageIndex}) ROWS FETCH NEXT LOWER(:#{#filter.pageSize}) ROWS ONLY ")
List<TableBItems> findByFilter(@Param(value = "filter") TBFilter filter);

这两个查询仅涉及的表、返回实体和参数实体不同,WHERE子句、排序及分页逻辑完全一致,请问是否有办法复用这些重复的代码部分?


可行的代码复用方案

针对这种仅表名、返回/参数实体不同,其余SQL逻辑完全一致的场景,有几种实用的复用方式:

1. 提取静态SQL片段常量

最直接的方式是把重复的SQL部分提取为静态常量,在两个查询中拼接使用,避免重复编写:

首先创建一个公共的SQL常量类,统一管理重复片段:

public class CommonSqlConstants {
    public static final String COMMON_WHERE_CLAUSE = "WHERE " +
            "(LOWER(NAME) LIKE NVL(CONCAT('%', CONCAT(LOWER(:#{#filter.name}), '%')), NAME) " +
            "OR LOWER(CODE) LIKE NVL(CONCAT('%', CONCAT(LOWER(:#{#filter.code}), '%')), CODE)) " +
            "AND (TO_CHAR(USER_WORKFLOW_ID) IN (:#{#filter.userWorkFlowIds}) OR COALESCE(:#{#filter.userWorkFlowIds}, NULL) IS NULL) ";

    public static final String COMMON_ORDER_PAGINATION = "ORDER BY " +
            "      CASE " +
            "         WHEN :#{#filter.orderByColumn.index} = 4 AND :#{#filter.orderDirection.index} = -1 THEN VERSION " +
            "      END DESC, " +
            "      CASE " +
            "         WHEN :#{#filter.orderByColumn.index} = 10 AND :#{#filter.orderDirection.index} = -1 THEN PROGRESS " +
            "      END DESC " +
            "OFFSET LOWER(:#{#filter.pageSize} * :#{#filter.pageIndex}) ROWS FETCH NEXT LOWER(:#{#filter.pageSize}) ROWS ONLY ";
}

然后在具体的Repository中引用这些常量:

public interface TableARepository extends JpaRepository<TableAItems, Long> {
    @Query(nativeQuery = true,
            value = "SELECT TA.* FROM TABLE_A TA " + CommonSqlConstants.COMMON_WHERE_CLAUSE + CommonSqlConstants.COMMON_ORDER_PAGINATION)
    List<TableAItems> findByFilter(@Param(value = "filter") TAFilter filter);
}
public interface TableBRepository extends JpaRepository<TableBItems, Long> {
    @Query(nativeQuery = true,
            value = "SELECT TB.* FROM TABLE_B TB " + CommonSqlConstants.COMMON_WHERE_CLAUSE + CommonSqlConstants.COMMON_ORDER_PAGINATION)
    List<TableBItems> findByFilter(@Param(value = "filter") TBFilter filter);
}

这种方式简单易维护,适合少量类似查询的场景。

2. 自定义通用Repository基类

如果后续还有更多类似表需要复用相同逻辑,可以通过泛型封装通用Repository基类:

步骤1:定义通用Filter接口

让TAFilter和TBFilter实现该接口,确保两者包含查询所需的所有字段:

public interface BaseFilter {
    String getName();
    String getCode();
    List<String> getUserWorkFlowIds();
    OrderColumn getOrderByColumn();
    OrderDirection getOrderDirection();
    Integer getPageSize();
    Integer getPageIndex();
}

// TAFilter实现示例
public class TAFilter implements BaseFilter {
    // 实现接口所有方法
}

// TBFilter实现示例
public class TBFilter implements BaseFilter {
    // 实现接口所有方法
}

步骤2:定义通用Repository接口

@NoRepositoryBean
public interface BaseRepository<T, ID, F extends BaseFilter> extends JpaRepository<T, ID> {
    List<T> findByFilter(F filter);
}

步骤3:实现通用Repository逻辑

public class BaseRepositoryImpl<T, ID, F extends BaseFilter> extends SimpleJpaRepository<T, ID> implements BaseRepository<T, ID, F> {
    private final EntityManager entityManager;
    private final Class<T> entityClass;

    public BaseRepositoryImpl(Class<T> entityClass, EntityManager entityManager) {
        super(entityClass, entityManager);
        this.entityClass = entityClass;
        this.entityManager = entityManager;
    }

    @Override
    public List<T> findByFilter(F filter) {
        // 从实体的@Table注解获取表名
        Table tableAnnotation = entityClass.getAnnotation(Table.class);
        String tableName = tableAnnotation.name();
        // 生成实体别名(比如TABLE_A取TA)
        String entityAlias = tableName.substring(0, 2).toUpperCase();

        // 拼接完整SQL
        String sql = String.format("SELECT %s.* FROM %s %s %s %s",
                entityAlias,
                tableName,
                entityAlias,
                CommonSqlConstants.COMMON_WHERE_CLAUSE,
                CommonSqlConstants.COMMON_ORDER_PAGINATION);

        // 创建原生查询并绑定参数
        Query query = entityManager.createNativeQuery(sql, entityClass);
        query.setParameter("filter", filter);

        return query.getResultList();
    }
}

步骤4:配置并使用通用Repository

在Spring启动类中指定自定义Repository基类:

@EnableJpaRepositories(repositoryBaseClass = BaseRepositoryImpl.class)
@SpringBootApplication
public class Application {
    // ...
}

然后让具体Repository继承通用基类:

public interface TableARepository extends BaseRepository<TableAItems, Long, TAFilter> {
}

public interface TableBRepository extends BaseRepository<TableBItems, Long, TBFilter> {
}

这种方式适合大量类似表的场景,一次性封装通用逻辑,后续新增表只需继承基类即可。


内容的提问来源于stack exchange,提问作者nemostyle

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 02:24:12