0

J'ai un projet avec 3 DataSources différentes. Il fonctionne très bien si le projet est exécuté à partir avec le printemps-boot: exécuter uniquement avec ces dépendances:L'ajout de spring-boot-starter-web aux dépendances casse plusieurs sources de données

<parent> 
    <groupId>org.springframework.boot</groupId> 
    <artifactId>spring-boot-starter-parent</artifactId> 
    <version>1.2.6.RELEASE</version> 
</parent> 
<dependencies> 

    <dependency> 
     <groupId>org.springframework.boot</groupId> 
     <artifactId>spring-boot-configuration-processor</artifactId> 
     <optional>true</optional> 
    </dependency> 

    <dependency> 
     <groupId>org.springframework.boot</groupId> 
     <artifactId>spring-boot-starter-data-jpa</artifactId> 
    </dependency> 

    <dependency> 
     <groupId>org.postgresql</groupId> 
     <artifactId>postgresql</artifactId> 
     <version>RELEASE</version> 
    </dependency> 

    <!--<dependency>--> 
     <!--<groupId>org.springframework.boot</groupId>--> 
     <!--<artifactId>spring-boot-starter-web</artifactId>--> 
    <!--</dependency>--> 

Voici une source de données, ils sont tous à peu près les mêmes, que la modification des informations de noms de haricots et base de données

@Configuration 
@EnableTransactionManagement 
@EnableJpaRepositories(entityManagerFactoryRef = "emfIntranet", transactionManagerRef = "tmIntranet", basePackages = {"com.vnt.intranet.repositories"}) 
@ConfigurationProperties(prefix = "databases.sistemas") 
public class IntranetPersistence { 

    private String address; 
    private String schema; 
    private String username; 
    private String password; 
    private String eclipselinklog; 
    private Boolean sqllog; 

    @Primary 
    @Bean(name = "dsIntranet") 
    public DataSource dataSource() { 
     org.apache.tomcat.jdbc.pool.DataSource dataSource = new org.apache.tomcat.jdbc.pool.DataSource(); 
     dataSource.setUrl("jdbc:postgresql://" + address + "/" + schema); 
     dataSource.setUsername(username); 
     dataSource.setPassword(password); 
     dataSource.setDriverClassName("org.postgresql.Driver"); 
     dataSource.setInitialSize(3); 
     dataSource.setMaxIdle(10); 
     dataSource.setMaxActive(10); 
     return dataSource; 
    } 

    private EclipseLinkJpaVendorAdapter getEclipseLinkJpaVendorAdapter() { 
     EclipseLinkJpaVendorAdapter vendorAdapter = new EclipseLinkJpaVendorAdapter(); 
     vendorAdapter.setDatabasePlatform("org.eclipse.persistence.platform.database.PostgreSQLPlatform"); 
     vendorAdapter.setShowSql(sqllog); 
     return vendorAdapter; 
    } 

    @Primary 
    @Bean(name = "emfIntranet") 
    public EntityManagerFactory entityManagerFactory() { 
     LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean(); 
     factoryBean.setJpaVendorAdapter(getEclipseLinkJpaVendorAdapter()); 
     factoryBean.setDataSource(dataSource()); 
     factoryBean.setPackagesToScan("com.vnt.intranet.entities"); 
     factoryBean.setPersistenceUnitName("intranet"); 

     Properties jpaProperties = new Properties(); 
     jpaProperties.put("eclipselink.weaving", "false"); 
     jpaProperties.put("eclipselink.logging.level", eclipselinklog); // SEVERE/FINEST 

     factoryBean.setJpaProperties(jpaProperties); 
     factoryBean.afterPropertiesSet(); 
     return factoryBean.getObject(); 
    } 

    @Primary 
    @Bean(name = "tmIntranet") 
    public PlatformTransactionManager transactionManager() { 
     JpaTransactionManager transactionManager = new JpaTransactionManager(); 
     transactionManager.setEntityManagerFactory(entityManagerFactory()); 
     return transactionManager; 
    } 

    public String getAddress() { 
     return address; 
    } 

    public void setAddress(String address) { 
     this.address = address; 
    } 

    public String getSchema() { 
     return schema; 
    } 

    public void setSchema(String schema) { 
     this.schema = schema; 
    } 

    public String getUsername() { 
     return username; 
    } 

    public void setUsername(String username) { 
     this.username = username; 
    } 

    public String getPassword() { 
     return password; 
    } 

    public void setPassword(String password) { 
     this.password = password; 
    } 

    public String getEclipselinklog() { 
     return eclipselinklog; 
    } 

    public void setEclipselinklog(String eclipselinklog) { 
     this.eclipselinklog = eclipselinklog; 
    } 

    public Boolean getSqllog() { 
     return sqllog; 
    } 

    public void setSqllog(Boolean sqllog) { 
     this.sqllog = sqllog; 
    } 
} 

Je peux accéder à toutes les sources de données sans problème ... L'une d'entre elles est annotée avec @Primary.

Mais si je décommenter la dépendance printemps-boot-démarreur-web, il se brise et il me donne:

Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined: more than one 'primary' bean found among candidates: [emfIntranet, entityManagerFactory, emfMkRadius, emfMkData] 

Je suis en train de convertir à un projet web sans succès ...

Des idées?

EDIT Ajout d'autres classes pour plus de clarté:

MkDataPersistence.class

@Configuration 
@EnableTransactionManagement 
@EnableJpaRepositories(entityManagerFactoryRef = "emfMkData", transactionManagerRef = "tmMkData", basePackages = {"org.example.mkdata.repositories"}) 
@ConfigurationProperties(prefix = "databases.mkdata") 
public class MkDataPersistence { 

    private String address; 
    private String schema; 
    private String username; 
    private String password; 
    private String eclipselinklog; 
    private Boolean sqllog; 

    @Bean(name = "dsMkData") 
    javax.sql.DataSource dataSource() { 
     DataSource dataSource = new DataSource(); 
     dataSource.setUrl("jdbc:postgresql://" + address + "/" + schema); 
     dataSource.setUsername(username); 
     dataSource.setPassword(password); 
     dataSource.setDriverClassName("org.postgresql.Driver"); 
     dataSource.setInitialSize(3); 
     dataSource.setMaxIdle(10); 
     dataSource.setMaxActive(10); 
     return dataSource; 
    } 

    @Bean 
    HibernateJpaVendorAdapter getHibernateJpaVendorAdapter() { 
     HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); 
     vendorAdapter.setDatabasePlatform("org.hibernate.dialect.PostgreSQL9Dialect"); 
     vendorAdapter.setShowSql(sqllog); 
     return vendorAdapter; 
    } 

    @Bean(name = "emfMkData") 
    EntityManagerFactory entityManagerFactory() { 
     LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean(); 
     factoryBean.setJpaVendorAdapter(getHibernateJpaVendorAdapter()); 
     factoryBean.setDataSource(dataSource()); 
     factoryBean.setPackagesToScan("org.example.mkdata.entities"); 
     factoryBean.setPersistenceUnitName("mkdata"); 

     Properties jpaProperties = new Properties(); 
     jpaProperties.put("eclipselink.weaving", "false"); 
     jpaProperties.put("eclipselink.logging.level", eclipselinklog); // SEVERE/FINEST 

     factoryBean.setJpaProperties(jpaProperties); 
     factoryBean.afterPropertiesSet(); 
     return factoryBean.getObject(); 
    } 

    @Bean(name = "tmMkData") 
    PlatformTransactionManager transactionManager() { 
     JpaTransactionManager transactionManager = new JpaTransactionManager(); 
     transactionManager.setEntityManagerFactory(entityManagerFactory()); 
     return transactionManager; 
    } 

    public String getAddress() { 
     return address; 
    } 

    public void setAddress(String address) { 
     this.address = address; 
    } 

    public String getSchema() { 
     return schema; 
    } 

    public void setSchema(String schema) { 
     this.schema = schema; 
    } 

    public String getUsername() { 
     return username; 
    } 

    public void setUsername(String username) { 
     this.username = username; 
    } 

    public String getPassword() { 
     return password; 
    } 

    public void setPassword(String password) { 
     this.password = password; 
    } 

    public String getEclipselinklog() { 
     return eclipselinklog; 
    } 

    public void setEclipselinklog(String eclipselinklog) { 
     this.eclipselinklog = eclipselinklog; 
    } 

    public Boolean getSqllog() { 
     return sqllog; 
    } 

    public void setSqllog(Boolean sqllog) { 
     this.sqllog = sqllog; 
    } 
} 

MkRadiusPersistence.class

@Configuration 
@EnableTransactionManagement() 
@EnableJpaRepositories(entityManagerFactoryRef = "emfMkRadius", transactionManagerRef = "tmMkRadius", basePackages = {"org.example.mkradius.repositories"}) 
@ConfigurationProperties(prefix = "databases.mkradius") 
public class MkRadiusPersistence { 

    private String address; 
    private String schema; 
    private String username; 
    private String password; 
    private String eclipselinklog; 
    private Boolean sqllog; 

    @Bean(name = "dsMkRadius") 
    javax.sql.DataSource dataSource() { 
     DataSource dataSource = new DataSource(); 
     dataSource.setUrl("jdbc:postgresql://" + address + "/" + schema); 
     dataSource.setUsername(username); 
     dataSource.setPassword(password); 
     dataSource.setDriverClassName("org.postgresql.Driver"); 
     dataSource.setInitialSize(3); 
     dataSource.setMaxIdle(10); 
     dataSource.setMaxActive(10); 
     return dataSource; 
    } 

    @Bean 
    HibernateJpaVendorAdapter getHibernateJpaVendorAdapter() { 
     HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); 
     vendorAdapter.setDatabasePlatform("org.hibernate.dialect.PostgreSQL9Dialect"); 
     vendorAdapter.setShowSql(sqllog); 
     return vendorAdapter; 
    } 

    @Bean(name = "emfMkRadius") 
    EntityManagerFactory entityManagerFactory() { 
     LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean(); 
     factoryBean.setJpaVendorAdapter(getHibernateJpaVendorAdapter()); 
     factoryBean.setDataSource(dataSource()); 
     factoryBean.setPackagesToScan("org.example.mkradius.entities"); 
     factoryBean.setPersistenceUnitName("mkradius"); 

     Properties jpaProperties = new Properties(); 
     jpaProperties.put("eclipselink.weaving", "false"); 
     jpaProperties.put("eclipselink.logging.level", eclipselinklog); // SEVERE/FINEST 

     factoryBean.setJpaProperties(jpaProperties); 
     factoryBean.afterPropertiesSet(); 
     return factoryBean.getObject(); 
    } 

    @Bean(name = "tmMkRadius") 
    PlatformTransactionManager transactionManager() { 
     JpaTransactionManager transactionManager = new JpaTransactionManager(); 
     transactionManager.setEntityManagerFactory(entityManagerFactory()); 
     return transactionManager; 
    } 

    public String getAddress() { 
     return address; 
    } 

    public void setAddress(String address) { 
     this.address = address; 
    } 

    public String getSchema() { 
     return schema; 
    } 

    public void setSchema(String schema) { 
     this.schema = schema; 
    } 

    public String getUsername() { 
     return username; 
    } 

    public void setUsername(String username) { 
     this.username = username; 
    } 

    public String getPassword() { 
     return password; 
    } 

    public void setPassword(String password) { 
     this.password = password; 
    } 

    public String getEclipselinklog() { 
     return eclipselinklog; 
    } 

    public void setEclipselinklog(String eclipselinklog) { 
     this.eclipselinklog = eclipselinklog; 
    } 

    public Boolean getSqllog() { 
     return sqllog; 
    } 

    public void setSqllog(Boolean sqllog) { 
     this.sqllog = sqllog; 
    } 
} 

EDIT 2

Application.class

@Configuration 
@ComponentScan(basePackages = { "org.example.startup" }) 
@EnableAutoConfiguration 
public class Application { 
    private static final Logger logger = LoggerFactory.getLogger(Application.class); 

    @Autowired 
    CableRouteRepository cableRouteRepository; 

    @Autowired 
    CityRepository cityRepository; 

    @Autowired 
    RadAcctRepository radAcctRepository; 

    public static void main(String[] args) { 

     ConfigurableApplicationContext context = new SpringApplicationBuilder() 
       .showBanner(false) 
       .sources(Application.class) 
       .run(args); 

     Application app = context.getBean(Application.class); 

//  for (String bean: context.getBeanDefinitionNames()) { 
//   logger.info(bean); 
//  } 

     app.start(); 
    } 

    private void start() { 
     logger.info("Application.start()"); 

     logger.info("{}", cableRouteRepository.findAll()); 
     logger.info("{}", cityRepository.findAll()); 
     logger.info("{}", radAcctRepository.findTest()); 
    } 


} 

C'est la classe de démarrage ... J'ai imprimé tous les dépôts comme un test (chaque référentiel est ici sur un autre DataSource) ... Ils fonctionnent très bien tant que je ne suis pas avoir printemps-starter-web sur le classpath.

EDIT 3

Github repo https://github.com/mtrojahn/test-multiple-databases

J'espère que je l'ai fait droit ... Je ne ai jamais vraiment travaillé avec Github :)

EDIT 4

Github mis à jour correctement avec le code défaillant.

Pour rappel, si le soufflet de dépendance est commenté, le code fonctionne:

<dependency> 
    <groupId>org.springframework.boot</groupId> 
    <artifactId>spring-boot-starter-web</artifactId> 
</dependency> 

Et si la dépendance ci-dessus est décommentée mais le soufflet de code est modifié dans le IntranetPersistence.classe de:

@Primary 
@Bean(name = "emfIntranet") 

à

@Primary 
@Bean(name = "entityManagerFactory") 

Il remplace le haricot par défaut et commence à échouer avec:

Caused by: java.lang.IllegalArgumentException: Not an managed type: class org.example.intranet.entities.CableRoute 
+0

Où sont '' emfMkRadius' et emfMkData' déclaré? Je peux voir que 'emfIntranet' est' @ Primary ', est l'un des deux autres aussi? –

+0

Je vais éditer avec eux, mais ils sont à peu près les mêmes, d'où la raison pour laquelle je ne les ai pas tous postés. –

+0

Le bean 'entityManagerFactory' de Spring Boot est' @ Primary' mais il aurait dû être désactivé par l'un de vos beans 'EntityManagerFactory' car sa création est conditionnelle à ce qu'il ne soit pas un bean' EntityManagerFactory' dans le contexte. A quoi ressemble votre classe principale? Vous référez-vous directement à l'une des classes de configuration automatique de Boot? –

Répondre

1

Vous êtes affecté par le comportement du printemps Boot 1.2 de Auto-configuration JPA. Il ne fait que désactiver la création de son propre bean entityManagerFactory s'il existe un LocalContainerEntityManagerFactoryBean défini par l'utilisateur. Vous utilisez LocalContainerEntityManagerFactoryBean mais en appelant afterPropertiesSet et getObject vous-même plutôt que de permettre au conteneur de le faire pour vous. Cela laisse le contexte sera plusieurs haricots @PrimaryEntityManagerFactory. Cela a été improved in Spring Boot 1.3 de sorte qu'un utilisateur déclaré EntityManagerFactory bean également désactiver la configuration automatique.

Cela provoque un problème lorsque vous essayez de créer openEntityManagerInViewInterceptor car il a besoin d'un EntityManagerFactory et le contexte n'a aucun moyen de savoir lequel des deux haricots @Primary il devrait choisir.

Il existe plusieurs façons de procéder. Vous pouvez mettre à jour votre configuration pour définir des beans de type LocalContainerEntityManagerFactoryBean plutôt que EntityManagerFactory. Une autre est de désactiver la création de l'intercepteur en ajoutant ce qui suit à votre application.yml:

spring: 
    jpa: 
    open_in_view: false 
+0

Oui, cela semble le réparer. Merci pour votre temps et votre intérêt :) –

+0

@Coderdood J'ai mis à jour ma réponse avec plus de détails sur ce qui se passe et pourquoi. –

+0

Oui, j'ai changé mon bean en LocalContainerEntityManagerFactoryBean et cela a fait l'affaire. Fonctionne même avec spring.jpa.open_in_view = true maintenant. Merci! –