Spring Boot - JpaRepository не инициализируется при использовании нескольких источников данных - PullRequest
0 голосов
/ 14 декабря 2018

У меня есть вопрос, касающийся Spring Boot и использования установки с несколькими источниками данных с использованием JpaRepositories.

[ 4 редактирования ниже ]

Структура проекта выглядит следующим образомthis: 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.

Что я упустил из виду, так это то, что рассматриваемая службатребуется подтип JpaRepository, реализующий интерфейс Spring Security UserDetailsManager.Похоже, что система Spring Security пытается создать экземпляр службы UserDetailsManager до того, как могут быть созданы компоненты JpaRepository, выбранные @EnableJpaRepositories.

Есть ли какое-то решение для этого?

--- Edit 2 ---

Я попытался @Import класс AJpaConfiguration, который ничего не изменил.Однако, просматривая журналы создания экземпляров, я обнаружил следующие сообщения:

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 выбирает аннотацию @Bean для LocalContainerEntityManagerFactoryBean, но не создает ни экземпляры EntityManagerFactory, ни собирает @Аннотация Bean для PlatformTransactionManager.

--- Edit 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-компоненты перед всем остальным?Казалось бы, это решит проблему.

--- Edit 4 ---

Благодаря подсказке @RobertNiestroj я добавил @Lazy в @Autowired аннотаций, которые приводят к их инициализации после репозиториев.

Но теперь появляется другая ошибка:
java.lang.IllegalArgumentException: interface com.mycompany.schema.domain_A.repository.ARepository is not visible from class loader

Я должен также упомянуть, что это приложение построено изМодули Maven: - Модуль схемы (конфигурация JPA, репозитории и классы сущностей) - Основной модуль (конфигурация приложения и классы свойств) - [Другие модули, использующие ядро ​​и схему] - Главный модуль (класс приложения Spring-Boot)

1 Ответ

0 голосов
/ 14 декабря 2018

Пожалуйста, попробуйте автоматическую разводку, как показано ниже, и проверьте

@Service
public class MyService {

    @Autowired
    private ARepository repository;

    // ...
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...