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

Spring Boot手动Autowiring失效原因:多数据库延迟连接问题

问题描述

现有配置

我正在开发一个Spring Boot应用,已配置多数据库(Oracle、Teradata等),通过@Entity、Repository类及带有@EnableJpaRepositories注解的配置类完成初始化,示例配置类如下:

@Configuration
@EnableJpaRepositories(
    basePackages = "com.baeldung.multipledb.dao.product", 
    entityManagerFactoryRef = "productEntityManager", 
    transactionManagerRef = "productTransactionManager"
)
public class PersistenceProductConfiguration {

    @Bean
    public LocalContainerEntityManagerFactoryBean productEntityManager() {
        LocalContainerEntityManagerFactoryBean em
          = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(productDataSource());
        em.setPackagesToScan(
          new String[] { "com.baeldung.multipledb.model.product" });

        
        em.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
        HashMap<String, Object> properties = new HashMap<>();
        properties.put("hibernate.hbm2ddl.auto",
          env.getProperty("hibernate.hbm2ddl.auto"));
        properties.put("hibernate.dialect", env.getProperty("hibernate.dialect"));
        em.setJpaPropertyMap(properties);

        return em;
    }

    @Bean
    @ConfigurationProperties(prefix = "datasource.dbprops")
    public DataSource productDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    public PlatformTransactionManager productTransactionManager() {
        JpaTransactionManager transactionManager
          = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(
          productEntityManager().getObject());
        return transactionManager;
    }
}

需求

现有配置会在启动时连接数据库,我希望在启动后,通过Controller接收用户请求时再建立数据库连接,示例Controller逻辑如下:

/*LOGIC BELOW IS ALTERNATIVE TO CLASS PersistanceProductConfiguration ABOVE*/
@RestController
public class RestController {

    @GetMapping(value = "/student/{studentId}")
    public Student getTestData(@PathVariable Integer studentId) {
        ensureConnectionExists();
        return ResponseEntity.ok("");
    }

    public void ensureConnectionExists() {
        if(!connectionExists){
        //================
        // ESTABLISH THE CONNECTION HERE
        //=================
        }
    }
}

尝试的实现

@Autowired
private ApplicationContext applicationContext;


public void ensureConnectionExists() {
    if(!connectionExists){


    DataSource datasource = DataSourceBuilder.create()
    .username("jdbc.user")
    .password("jdbc.pass")
    .url("jdbc.url")
    .driverClassName("jdbc.driver")
    .build();

    LocalContainerEntityManagerFactoryBean em
          = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(productDataSource());
        em.setPackagesToScan(
          new String[] { "com.baeldung.multipledb.model.product" });

        
        em.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
        HashMap<String, Object> properties = new HashMap<>();
        properties.put("hibernate.hbm2ddl.auto",
          env.getProperty("hibernate.hbm2ddl.auto"));
        properties.put("hibernate.dialect", env.getProperty("hibernate.dialect"));
        em.setJpaPropertyMap(properties);

        JpaTransactionManager transactionManager
          = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(
          productEntityManager().getObject());
        

        AutowireCapableBeanFactory factory = applicationContext.getAutowireCapableBeanFactory();
        factory.autowireBean(transactionManager);
        factory.initializeBean(transactionManager, "JpaTransactionManager");


    }
}

遇到的问题

上述手动Autowiring的代码未生效,断点能命中,但Bean未完成自动装配,也未成功建立数据库连接。遗漏了什么?


解决方案

你当前的实现存在几个关键问题,导致Bean无法正确初始化和数据库连接无法建立:

  1. EntityManagerFactory未完成初始化
    LocalContainerEntityManagerFactoryBean是FactoryBean,直接创建实例后必须调用afterPropertiesSet()触发初始化流程,否则无法生成可用的EntityManagerFactory实例,后续事务管理器拿到的是无效对象。

  2. DataSource引用错误
    你手动构建了datasource,但后续em.setDataSource(productDataSource())仍调用原配置类的Bean方法,会重新创建一个新的DataSource,而非你自定义的实例。

  3. 未将Bean注册到Spring容器
    手动创建的EntityManagerFactory和TransactionManager没有注册到Spring容器,导致Repository无法关联到这些Bean,@EnableJpaRepositories也无法识别新实例。

  4. Environment未注入
    代码中直接使用env.getProperty(),但Environment未注入到Controller,会触发空指针异常,无法获取Hibernate配置参数。

修正后的实现步骤

步骤1:完善Controller依赖注入

@RestController
public class RestController {
    @Autowired
    private ApplicationContext applicationContext;
    @Autowired
    private Environment env;
    private boolean connectionExists = false;
    // 缓存已初始化的Bean实例
    private LocalContainerEntityManagerFactoryBean productEntityManager;
    private JpaTransactionManager productTransactionManager;

    @GetMapping(value = "/student/{studentId}")
    public ResponseEntity<String> getTestData(@PathVariable Integer studentId) {
        ensureConnectionExists();
        return ResponseEntity.ok("");
    }
}

步骤2:修正ensureConnectionExists方法

// 添加同步锁避免多请求重复初始化
public synchronized void ensureConnectionExists() {
    if(!connectionExists){
        // 1. 构建自定义DataSource
        DataSource datasource = DataSourceBuilder.create()
                .username(env.getProperty("jdbc.user"))
                .password(env.getProperty("jdbc.pass"))
                .url(env.getProperty("jdbc.url"))
                .driverClassName(env.getProperty("jdbc.driver"))
                .build();

        // 2. 构建并初始化EntityManagerFactoryBean
        productEntityManager = new LocalContainerEntityManagerFactoryBean();
        productEntityManager.setDataSource(datasource);
        productEntityManager.setPackagesToScan("com.baeldung.multipledb.model.product");
        productEntityManager.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
        
        HashMap<String, Object> properties = new HashMap<>();
        properties.put("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
        properties.put("hibernate.dialect", env.getProperty("hibernate.dialect"));
        // 添加延迟连接配置,避免提前建立连接
        properties.put("hibernate.connection.provider_disables_autocommit", true);
        properties.put("hibernate.hikari.auto-commit", false);
        
        productEntityManager.setJpaPropertyMap(properties);
        // 触发FactoryBean初始化
        productEntityManager.afterPropertiesSet();

        // 3. 构建并初始化事务管理器
        productTransactionManager = new JpaTransactionManager();
        productTransactionManager.setEntityManagerFactory(productEntityManager.getObject());
        
        AutowireCapableBeanFactory factory = applicationContext.getAutowireCapableBeanFactory();
        factory.autowireBean(productTransactionManager);
        factory.initializeBean(productTransactionManager, "productTransactionManager");

        // 4. 将Bean注册到Spring容器,供Repository关联使用
        factory.registerSingleton("productEntityManager", productEntityManager.getObject());
        factory.registerSingleton("productTransactionManager", productTransactionManager);

        connectionExists = true;
    }
}

步骤3:调整原配置类为懒加载

原配置类需要设置懒加载,避免启动时自动初始化:

@Configuration
@Lazy
@EnableJpaRepositories(
    basePackages = "com.baeldung.multipledb.dao.product", 
    entityManagerFactoryRef = "productEntityManager", 
    transactionManagerRef = "productTransactionManager"
)
public class PersistenceProductConfiguration {
    @Bean
    @Lazy
    public LocalContainerEntityManagerFactoryBean productEntityManager() {
        // 原代码保持不变
    }

    @Bean
    @Lazy
    @ConfigurationProperties(prefix = "datasource.dbprops")
    public DataSource productDataSource() {
        // 原代码保持不变
    }

    @Bean
    @Lazy
    public PlatformTransactionManager productTransactionManager() {
        // 原代码保持不变
    }
}

额外注意事项

  • 线程安全:必须给ensureConnectionExists()添加synchronized关键字,防止多请求同时触发重复初始化。
  • 连接池配置:确保DataSource连接池设置了合理的超时时间和最小连接数,避免连接闲置失效。
  • Repository懒加载:Repository接口需配合@Lazy注解使用,避免启动时触发数据库连接校验。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 17:53:13