【问题标题】:Spring Boot - JpaRepository not initializing when using multiple DataSourcesSpring Boot - 使用多个数据源时 JpaRepository 未初始化
【发布时间】:2019-05-15 13:08:46
【问题描述】:

我有一个关于 Spring Boot 和使用 JpaRepositories 使用具有多个数据源的设置的问题。

[下面有 4 个编辑]

项目的结构如下所示: com/mycompany/schema/AbstractJpaConfig com/mycompany/schema/domain_A/AJpaConfiguration com/mycompany/schema/domain_A/entity/AEntity com/mycompany/schema/domain_A/repository/ARepository com/mycompany/schema/domain_B/BJpaConfiguration com/mycompany/schema/domain_B/entity/BEntity com/mycompany/schema/domain_B/repository/BRepository

所以我有两个域(A 和 B),分别处理 DataSource 设置。

抽象 JPA 配置类用于减少冗余并使用处理数据源的自定义 DataSourceManager:

public abstract class AbstractJpaConfiguration {

    private final DataSourceManager dataSources;

    public AbstractJpaConfiguration(DataSourceManager dataSources) {
        this.dataSources = dataSources;
    }

    protected abstract String persistenceUnitName();

    protected abstract Class<?> packageEntityClass();

    protected abstract DataSource useDataSource(DataSourceManager dataSources);

    public abstract LocalContainerEntityManagerFactoryBean entityManagerFactoryBean();

    public abstract PlatformTransactionManager transactionManagerBean();

    public abstract LocalContainerEntityManagerFactoryBean getEntityManagerFactoryBean();

    protected LocalContainerEntityManagerFactoryBean buildEntityManagerFactory() {
        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(useDataSource(dataSources));
        em.setPersistenceUnitName(persistenceUnitName() + "PU");
        em.setBeanName(persistenceUnitName() + "EntityManager");
        em.setPackagesToScan(packageEntityClass().getPackage().getName());
        em.setJpaPropertyMap(persistenceProperties());
        em.setJpaVendorAdapter(jpaVendorAdapter());
        return em;
    }

    protected Map<String, String> persistenceProperties() {
        Map<String, String> properties = new HashMap<>();
        properties.put("hibernate.hbm2ddl.auto", "validate");
        return properties;
    }

    protected JpaVendorAdapter jpaVendorAdapter() {
        HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
        adapter.setShowSql(false);
        adapter.setGenerateDdl(false);
        return adapter;
    }

    protected PlatformTransactionManager buildTransactionManager() {
        LocalContainerEntityManagerFactoryBean emfBean = getEntityManagerFactoryBean();
        EntityManagerFactory emf = emfBean.getObject();
        return new JpaTransactionManager(emf);
    }
}

配置的实现,位于域 A 包中,如下所示:

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        basePackages = "com.mycompany.schema.domain_A",
        entityManagerFactoryRef = "AEntityManagerFactory",
        transactionManagerRef = "ATransactionManager")
@EntityScan(basePackages = "com.mycompany.schema.domain_A")
@DependsOn("flywayMigrationInitializer")
public class AJpaConfiguration extends AbstractJpaConfiguration {

    @Autowired
    public AJpaConfiguration(DataSourceManager dataSources) {
        super(dataSources);
    }

    @Override
    protected Class<?> packageEntityClass() {
        return getClass(); // This class is located in the entity class package
    }

    @Override
    protected String persistenceUnitName() {
        return "a";
    }

    @Override
    protected DataSource useDataSource(DataSourceManager dataSources) {
        return dataSources.domainADataSource();
    }

    @Bean("aEntityManagerFactory")
    @Override
    public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean() {
        return buildEntityManagerFactory();
    }

    @Bean("aTransactionManager")
    @Override
    public PlatformTransactionManager transactionManagerBean() {
        return buildTransactionManager();
    }

    @Override
    public LocalContainerEntityManagerFactoryBean getEntityManagerFactoryBean() {
        return entityManagerFactoryBean();
    }
}

然后,实际的存储库被定义为 JpaRepository:

@Repository
public interface ARepository extends JpaRepository<AEntity, Long> {
}

根据应用程序日志,这似乎可以工作:

2018-12-14 09:45:02.997  INFO 13867 --- [  restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode!
2018-12-14 09:45:02.997  INFO 13867 --- [  restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data repositories in DEFAULT mode.
2018-12-14 09:45:03.012  INFO 13867 --- [  restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 9ms. Found 1 repository interface.
2018-12-14 09:45:03.029  INFO 13867 --- [  restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode!
2018-12-14 09:45:03.029  INFO 13867 --- [  restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data repositories in DEFAULT mode.
2018-12-14 09:45:03.085  INFO 13867 --- [  restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 56ms. Found 1 repository interface.

在这之后,Flyway 迁移成功,持久性单元启动:

2018-12-14 09:45:06.459  INFO 13867 --- [  restartedMain] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [
    name: aPU
    ...]
2018-12-14 09:45:06.544  INFO 13867 --- [  restartedMain] org.hibernate.Version                    : HHH000412: Hibernate Core {5.3.7.Final}
2018-12-14 09:45:06.546  INFO 13867 --- [  restartedMain] org.hibernate.cfg.Environment            : HHH000206: hibernate.properties not found
2018-12-14 09:45:06.746  INFO 13867 --- [  restartedMain] o.hibernate.annotations.common.Version   : HCANN000001: Hibernate Commons Annotations {5.0.4.Final}
2018-12-14 09:45:06.922  INFO 13867 --- [  restartedMain] org.hibernate.dialect.Dialect            : HHH000400: Using dialect: org.hibernate.dialect.MySQL57Dialect
2018-12-14 09:45:07.976  INFO 13867 --- [  restartedMain] o.h.h.i.QueryTranslatorFactoryInitiator  : HHH000397: Using ASTQueryTranslatorFactory
2018-12-14 09:45:08.066  INFO 13867 --- [  restartedMain] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'aPU'

但是,当我尝试将存储库自动装配到服务中时:

@Service
public class MyService {
    private final ARepository repository;

    @Autowired
    public MyService(ARepository repository) {
        this.repository = repository;
    }

    // ...
}

这是我在日志中收到的错误消息:

Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'MyService' defined in URL [jar:file:...]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.mycompany.schema.domain_A.repository.ARepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:767) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:218) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1308) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1154) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:538) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:498) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:273) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1239) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1166) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:855) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:758) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    ... 106 common frames omitted
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.mycompany.schema.domain_A.repository.ARepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1646) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1205) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1166) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:855) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:758) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    ... 120 common frames omitted

我希望提供的信息可以帮助解决这个问题。
如果没有,我很乐意提供更多信息。

感谢您的宝贵时间,祝您有美好的一天
- 亚历山大

--- 编辑---

我在配置类中放了两个断点,得到了一些新信息:

调用了创建LocalContainerEntityManagerFactoryBean的方法,但是在调用PlatformTransactionManager bean方法之前,应用程序由于缺少bean而停止。

我忽略的是,需要 JpaRepository 子类型的服务正在实现 Spring Security UserDetailsManager 接口。 似乎 Spring Security 系统正在尝试在创建 @EnableJpaRepositories 拾取的 JpaRepository Bean 之前实例化 UserDetailsManager 服务。

有什么解决办法吗?

--- 编辑 2 ---

我尝试@Import AJpaConfiguration 类,它没有改变任何东西。 但是,仔细查看 bean 实例化日志,我发现了以下消息:

2018-12-14 11:52:08.395 DEBUG 22417 --- [  restartedMain] o.s.b.f.s.DefaultListableBeanFactory     : Creating shared instance of singleton bean 'aEntityManagerFactory'
2018-12-14 11:52:08.395 DEBUG 22417 --- [  restartedMain] o.s.b.f.s.DefaultListableBeanFactory     : Creating shared instance of singleton bean 'aJpaConfig'

所以 Spring Boot 为LocalContainerEntityManagerFactoryBean 选择了@Bean 注解,但既没有创建 EntityManagerFactory 实例,也没有为PlatformTransactionManager 选择@Bean 注解。

--- 编辑 3 ---

我设置了 @Autowired 选项 required = false,现在它正在获取 bean 并实例化存储库 - 但仅在实例化需要它们的服务之后,因此不会注入它们。

2018-12-14 13:25:30.484 DEBUG 31932 --- [  restartedMain] o.s.b.f.s.DefaultListableBeanFactory     : Creating shared instance of singleton bean 'aEntityManager'
2018-12-14 13:25:33.035 DEBUG 31932 --- [  restartedMain] o.s.b.f.s.DefaultListableBeanFactory     : Creating shared instance of singleton bean 'aTransactionManager'
2018-12-14 13:25:34.852 DEBUG 31932 --- [  restartedMain] o.s.b.f.s.DefaultListableBeanFactory     : Creating shared instance of singleton bean 'aRepository'

我怎样才能先创建 bean?这似乎可以解决问题。

--- 编辑 4 ---

感谢@RobertNiestroj 的提示,我将@Lazy 添加到@Autowired 注释中,这导致它们在存储库之后被初始化。

但是现在,出现了不同的错误:
java.lang.IllegalArgumentException: interface com.mycompany.schema.domain_A.repository.ARepository is not visible from class loader

我还应该提到这个应用程序是由 Maven 模块构建的: - 模式模块(JPA 配置、存储库和实体类) - 核心模块(应用程序配置和属性类) - [使用Core和Schema的其他模块] - 主模块(Spring-Boot 应用程序类)

【问题讨论】:

  • 您可能想要发布Minimal, Complete, and Verifiable Examples。请尽量避免发布太多代码,这会使问题更难阅读。谢谢
  • 嗨,Alexander,您看到在日志中创建了ARepository 吗?在application.properties 中将您的日志设置为logging.level.org.springframework.beans.factory=DEBUG,然后查找“创建单例bean 'ARepository' 的共享实例”行。如果您没有看到该行,则表示创建 bean 时出现问题。即组件扫描没有找到它。
  • 谢谢@Rentius2407,我检查过了,存储库确实没有实例化。我会检查为什么会这样,以及如何创建它。
  • 使用@Rentius2407 提示调试后编辑
  • 尝试将@DependsOn("aRepository")添加到服务bean

标签: java spring spring-boot jpa spring-data-jpa


【解决方案1】:

您是否尝试过@Repository(name = "repository")? IIRC,通过不提供名称,使用与类匹配的名称创建组件,在本例中为“aRepository”。因此,在自动装配存储库变量时,没有名为“存储库”的组件,导致没有合格的 bean 异常。

【讨论】:

    【解决方案2】:

    请尝试如下自动装配并检查

    @Service
    public class MyService {
    
        @Autowired
        private ARepository repository;
    
        // ...
    }
    

    【讨论】:

    • 我试过了(没有final,因为需要在构造函数中设置final),但它不起作用——bean不存在,依赖关系不满足。错误仍然是No qualifying bean of type 'com.mycompany..schema.domain_A.repository.ARepository'
    • 从接口ARepository中移除注解@Repository并尝试
    • 还是没有豆子,抱歉。 :( 它正在创建 aEntityManagerFactoryaJpaConfig,但没有 TransactionManager 和 Repositories。
    • 等等——我设置了选项@Autowired(required = false),现在它正在创建存储库bean! - 但只有在所有其他bean之后,所以它们不会被注入到需要它们的服务中。我将编辑原始问题。
    猜你喜欢
    • 2018-12-11
    • 2017-09-21
    • 2016-12-16
    • 1970-01-01
    • 2021-12-03
    • 2018-02-14
    • 2016-08-23
    • 2018-03-22
    • 1970-01-01
    相关资源
    最近更新 更多