Веб-приложение Spring-Boot с несколькими базами данных JTA вызывает BeanCreationException

Я переношу существующее приложение командной строки Spring-Batch в веб-приложение, используя spring-boot-starter-web и spring-boot-starter-data-jpa вместе с spring-batch-admin (версия 1.3.0).

Я попытался настроить приложение так, чтобы оно могло работать с несколькими базами данных. После долгих проб и ошибок я наконец последовал этому примеру на основе JTA stackoverflow.com/questions/22779155/.

Но все же я снова и снова сталкиваюсь с одним и тем же BeanCreationException (см. StackTrace в конце поста).

Может ли возникнуть проблема из-за интеграции spring-batch-admin?

Вот как выглядит ServletInitializer:

@Configuration
@EnableAutoConfiguration(exclude = { BatchAutoConfiguration.class,
    DataSourceAutoConfiguration.class, WebMvcAutoConfiguration.class })
@Import(MainConfiguration.class)
public class BatchAdmin extends SpringBootServletInitializer {
      public static void main(String[] args) {
          SpringApplication.run(BatchAdmin.class, args);
      }
      // ...
}

Вот основные классы конфигурации, как это предлагается в упомянутой статье SO:

@Configuration
@ComponentScan("com.company.*")
@Import({ ServletConfiguration.class, WebappConfiguration.class, Db1Configuration.class,
    Db2Configuration.class })
@EnableTransactionManagement
public class MainConfiguration {

  @Bean
  public JpaVendorAdapter jpaVendorAdapter() {
    HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
    hibernateJpaVendorAdapter.setShowSql(true);
    hibernateJpaVendorAdapter.setGenerateDdl(true);
    hibernateJpaVendorAdapter.setDatabase(Database.POSTGRESQL);
    hibernateJpaVendorAdapter.setDatabasePlatform(MyPGDialect.class.getName());
    return hibernateJpaVendorAdapter;
  }

  @Bean(name = "userTransaction")
  public UserTransaction userTransaction() throws Throwable {
    UserTransactionImp userTransactionImp = new UserTransactionImp();
    userTransactionImp.setTransactionTimeout(10000);
    return userTransactionImp;
  }

  @Bean(name = "atomikosTransactionManager", initMethod = "init", destroyMethod = "close")
  public TransactionManager atomikosTransactionManager() throws Throwable {
    UserTransactionManager userTransactionManager = new UserTransactionManager();
    userTransactionManager.setForceShutdown(false);
    return userTransactionManager;
  }

  @Bean(name = "transactionManager")
  @DependsOn({ "userTransaction", "atomikosTransactionManager" })
  public PlatformTransactionManager transactionManager() throws Throwable {
    UserTransaction userTransaction = userTransaction();
    TransactionManager atomikosTransactionManager = atomikosTransactionManager();
    return new JtaTransactionManager(userTransaction, atomikosTransactionManager);
  }
}

И для каждой базы данных свой класс конфигурации:

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
  entityManagerFactoryRef = "entityManagerFactory1",
  transactionManagerRef = "transactionManager",
  basePackages = { "com.company.model.repository" })
@DependsOn("transactionManager")
public class Db1Configuration {

  @Autowired
  private JpaVendorAdapter jpaVendorAdapter;

  @Bean(name = "dataSource1", initMethod = "init", destroyMethod = "close")
  public DataSource osmDataSource() {
    PGXADataSource pgDataSource = new PGXADataSource();
    pgDataSource.setServerName("localhost");
    pgDataSource.setDatabaseName("db1");
    pgDataSource.setPortNumber(5432);
    pgDataSource.setUser("postgres");
    pgDataSource.setPassword("postgres");

    AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
    xaDataSource.setXaDataSource(pgDataSource);
    xaDataSource.setUniqueResourceName("datasource1");
    return xaDataSource;
  }

  @Bean(name = "entityManagerFactory1")
  public LocalContainerEntityManagerFactoryBean customerEntityManager() throws Throwable {
    LocalContainerEntityManagerFactoryBean entityManager = new LocalContainerEntityManagerFactoryBean();
    entityManager.setDataSource(osmDataSource());
    entityManager.setJpaVendorAdapter(jpaVendorAdapter);
    entityManager.setPackagesToScan("com.company.model.domain");
    entityManager.setPersistenceUnitName("persistenceUnit1");
    HashMap<String, Object> properties = new HashMap<String, Object>();
    properties.put("hibernate.transaction.jta.platform", AtomikosJtaPlatform.class.getName());
    // properties.put("javax.persistence.transactionType", "JTA");
    properties.put("hibernate.hbm2ddl.auto", "");
    entityManager.setJpaPropertyMap(properties);
    return entityManager;
  }
}

Вот что вызывает у меня головную боль:

Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'requestMappingHandlerMapping' defined in class path resource [org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration.class]: Instantiation of bean failed; nested exception is org.springframework.beans.factory.BeanDefinitionStoreException: Factory method [public org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport.requestMappingHandlerMapping()] threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'openEntityManagerInViewInterceptor' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration$JpaWebConfiguration.class]: Initialization of bean failed; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined: expected single matching bean but found 2: osm.entityManagerFactory,entityManagerFactory
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:597)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1094)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:989)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:504)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:703)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:760)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482)
    at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:120)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:691)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:320)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:952)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:941)
    at com.qompa.batch.admin.BatchAdmin.main(BatchAdmin.java:58)
Caused by: org.springframework.beans.factory.BeanDefinitionStoreException: Factory method [public org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport.requestMappingHandlerMapping()] threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'openEntityManagerInViewInterceptor' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration$JpaWebConfiguration.class]: Initialization of bean failed; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined: expected single matching bean but found 2: osm.entityManagerFactory,entityManagerFactory
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:188)
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:586)
    ... 17 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'openEntityManagerInViewInterceptor' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration$JpaWebConfiguration.class]: Initialization of bean failed; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined: expected single matching bean but found 2: osm.entityManagerFactory,entityManagerFactory
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:547)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
    at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:324)
    at org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration$JpaWebConfiguration$$EnhancerBySpringCGLIB$$e9895182.openEntityManagerInViewInterceptor(<generated>)
    at org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration$JpaWebConfiguration.addInterceptors(JpaBaseConfiguration.java:144)
    at org.springframework.web.servlet.config.annotation.WebMvcConfigurerComposite.addInterceptors(WebMvcConfigurerComposite.java:105)
    at org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration.addInterceptors(DelegatingWebMvcConfiguration.java:55)
    at org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport.getInterceptors(WebMvcConfigurationSupport.java:226)
    at org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport.requestMappingHandlerMapping(WebMvcConfigurationSupport.java:198)
    at org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration$$EnhancerBySpringCGLIB$$216f202d.CGLIB$requestMappingHandlerMapping$17(<generated>)
    at org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration$$EnhancerBySpringCGLIB$$216f202d$$FastClassBySpringCGLIB$$7252bcc7.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)
    at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:312)
    at org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration$$EnhancerBySpringCGLIB$$216f202d.requestMappingHandlerMapping(<generated>)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:166)
    ... 18 more
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined: expected single matching bean but found 2: osm.entityManagerFactory,entityManagerFactory
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:313)
    at org.springframework.orm.jpa.EntityManagerFactoryUtils.findEntityManagerFactory(EntityManagerFactoryUtils.java:142)
    at org.springframework.orm.jpa.EntityManagerFactoryAccessor.setBeanFactory(EntityManagerFactoryAccessor.java:137)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeAwareMethods(AbstractAutowireCapableBeanFactory.java:1572)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1540)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:539)
    ... 40 more

Есть идеи?


person achingfingers    schedule 05.09.2014    source источник


Ответы (1)


Если все ваши дао не имеют @Qualifier, чтобы указать, какой EntityManager использовать, вам придется пометить один из этих EntityManagerFactory с помощью @Primary.

    @Bean(name = "entityManagerFactory1")
    @Primary
    public LocalContainerEntityManagerFactoryBean ...

Скорее всего, вам придется сделать то же самое для вашего источника данных.

person gyoder    schedule 05.09.2014