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

Spring Boot中在PhysicalNamingStrategyStandardImpl中访问@Repository的问题

解决方案

你的问题核心是Hibernate初始化时机早于Spring Data JPA Repository:PhysicalNamingStrategy在JPA容器启动阶段就会被调用,而此时LookupRepo还在初始化流程中,导致循环依赖。下面是几个可行的解决思路:

1. 延迟获取Repository实例

用Spring的Provider接口延迟加载Repo,避免在初始化阶段直接注入依赖,直到真正需要查询时才获取实例:

@Component
public class LookupNamingStrategy extends PhysicalNamingStrategyStandardImpl {

    @Autowired
    private Provider<LookupRepo> repoProvider;

    @Override
    public Identifier toPhysicalTableName(Identifier logicalName, JdbcEnvironment context) {
        if(logicalName.getCanonicalName().equalsIgnoreCase("LOOKUP")) {
            return super.toPhysicalTableName(logicalName, context);
        }

        // 延迟获取Repo,此时Spring已完成Repo的初始化
        LookupRepo repo = repoProvider.get();
        LookUp lookUp = repo.findFirstByOrderById();
        return Identifier.toIdentifier(lookUp.getTable_name());
    }
}

2. 绕过Spring Data JPA,直接用JDBC查询

既然Repo还没准备好,直接用原生JDBC查询查找表,不需要依赖Spring Data的封装:

@Component
public class LookupNamingStrategy extends PhysicalNamingStrategyStandardImpl {

    @Autowired
    private DataSource dataSource;

    @Override
    public Identifier toPhysicalTableName(Identifier logicalName, JdbcEnvironment context) {
        if(logicalName.getCanonicalName().equalsIgnoreCase("LOOKUP")) {
            return super.toPhysicalTableName(logicalName, context);
        }

        String tableName = null;
        // 用原生JDBC查询查找表
        try (Connection conn = dataSource.getConnection()) {
            String sql = "SELECT table_name FROM lookup ORDER BY id LIMIT 1";
            try (PreparedStatement stmt = conn.prepareStatement(sql);
                 ResultSet rs = stmt.executeQuery()) {
                if(rs.next()) {
                    tableName = rs.getString("table_name");
                }
            }
        } catch (SQLException e) {
            throw new RuntimeException("查询表名失败", e);
        }

        // 若未查询到表名,返回默认逻辑名
        return tableName != null ? Identifier.toIdentifier(tableName) 
                                : super.toPhysicalTableName(logicalName, context);
    }
}

3. 提前加载表名到配置中

在Spring上下文初始化完成前,先查询表名并存入环境变量,后续NamingStrategy直接从配置读取:

第一步:写初始化类加载表名

@Component
public class TableNameInitializer implements ApplicationListener<ContextRefreshedEvent> {

    @Autowired
    private DataSource dataSource;

    @Autowired
    private ConfigurableEnvironment environment;

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        // 确保只执行一次(根上下文触发时执行)
        if(event.getApplicationContext().getParent() == null) {
            String tableName = queryTableName();
            if(tableName != null) {
                // 将表名存入环境变量,优先级最高
                Map<String, Object> props = Collections.singletonMap("dynamic.table.name", tableName);
                environment.getPropertySources().addFirst(new MapPropertySource("dynamicTableName", props));
            }
        }
    }

    private String queryTableName() {
        try (Connection conn = dataSource.getConnection()) {
            String sql = "SELECT table_name FROM lookup ORDER BY id LIMIT 1";
            try (PreparedStatement stmt = conn.prepareStatement(sql);
                 ResultSet rs = stmt.executeQuery()) {
                return rs.next() ? rs.getString("table_name") : null;
            }
        } catch (SQLException e) {
            throw new RuntimeException("预加载表名失败", e);
        }
    }
}

第二步:修改NamingStrategy读取配置

@Component
public class LookupNamingStrategy extends PhysicalNamingStrategyStandardImpl {

    @Autowired
    private Environment environment;

    @Override
    public Identifier toPhysicalTableName(Identifier logicalName, JdbcEnvironment context) {
        if(logicalName.getCanonicalName().equalsIgnoreCase("LOOKUP")) {
            return super.toPhysicalTableName(logicalName, context);
        }

        String tableName = environment.getProperty("dynamic.table.name");
        return tableName != null ? Identifier.toIdentifier(tableName) 
                                : super.toPhysicalTableName(logicalName, context);
    }
}

关于“检查Bean是否初始化”的思路

你提到的编程式检查Bean状态是可行的,但实用性不高:因为Hibernate只会在启动时调用一次toPhysicalTableName,如果此时Repo还在初始化,返回默认表名后就不会再更新了。如果非要尝试,可以实现BeanFactoryAware来获取BeanFactory,调用isSingletonCurrentlyInCreation方法判断,但不推荐作为最终解决方案。

内容的提问来源于stack exchange,提问作者ng.newbie

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 09:33:21