source multiple databases data java spring jpa spring-data spring-boot

java - multiple - Spring Boot, Spring Data JPA con mĂșltiples DataSources



spring connect multiple databases (6)

Estoy tratando de conectar cada @Repositorios a diferentes DataSource (s) con Spring Boot y Spring Data JPA. http://xantorohara.blogspot.com/2013/11/spring-boot-jdbc-with-multiple.html siguiente, http://xantorohara.blogspot.com/2013/11/spring-boot-jdbc-with-multiple.html , como referencia. Aquí está el código que estoy usando en un intento de implementar una solución similar usando Spring Data JPA.

CustomerDbConfig.java (primera conexión de origen de datos)

@Configuration @EnableJpaRepositories( entityManagerFactoryRef = "orderEntityManager", transactionManagerRef = "orderTransactionManager", basePackages = {"com.mm.repository.customer"}) public class CustomerDbConfig { @Bean(name = "customerEntityManager") public LocalContainerEntityManagerFactoryBean entityManagerFactory(){ LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); em.setDataSource(dataSource()); em.setPackagesToScan(new String[] {"com.mm.domain.customer"}); JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); em.setJpaVendorAdapter(vendorAdapter); em.setJpaProperties(additionalJpaProperties()); em.setPersistenceUnitName("customerPersistence"); em.setPackagesToScan("com.mm.domain.customer"); return em; } Properties additionalJpaProperties(){ Properties properties = new Properties(); properties.setProperty("hibernate.hbm2ddl.auto", "create-drop"); properties.setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect"); properties.setProperty("hibernate.show_sql", "true"); return properties; } @Bean public DataSource dataSource(){ return DataSourceBuilder.create() .url("jdbc:h2:mem:customer:H2") .driverClassName("org.h2.Driver") .username("sa") .password("") .build(); } @Bean(name = "customerTransactionManager") public PlatformTransactionManager transactionManager(EntityManagerFactory emf){ JpaTransactionManager transactionManager = new JpaTransactionManager(); transactionManager.setEntityManagerFactory(emf); return transactionManager; } }

CustomerDbConfig.java (Segunda fuente de datos)

@Configuration @EnableJpaRepositories( entityManagerFactoryRef = "orderEntityManager", transactionManagerRef = "orderTransactionManager", basePackages = {"com.mm.repository.customer"}) public class CustomerDbConfig { @Bean(name = "customerEntityManager") public LocalContainerEntityManagerFactoryBean entityManagerFactory(){ LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); em.setDataSource(dataSource()); em.setPackagesToScan(new String[] {"com.mm.domain.customer"}); JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); em.setJpaVendorAdapter(vendorAdapter); em.setJpaProperties(additionalJpaProperties()); em.setPersistenceUnitName("customerPersistence"); em.setPackagesToScan("com.mm.domain.customer"); return em; } Properties additionalJpaProperties(){ Properties properties = new Properties(); properties.setProperty("hibernate.hbm2ddl.auto", "create-drop"); properties.setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect"); properties.setProperty("hibernate.show_sql", "true"); return properties; } @Bean public DataSource dataSource(){ return DataSourceBuilder.create() .url("jdbc:h2:mem:customer:H2") .driverClassName("org.h2.Driver") .username("sa") .password("") .build(); } @Bean(name = "customerTransactionManager") public PlatformTransactionManager transactionManager(EntityManagerFactory emf){ JpaTransactionManager transactionManager = new JpaTransactionManager(); transactionManager.setEntityManagerFactory(emf); return transactionManager; } }

Customer.java (modelo)

@Entity @Table(name = "customer") @Data @EqualsAndHashCode(exclude = {"id"}) public class Customer { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Integer id; @Column(name = "name", nullable = false) private String name; @Column(name = "age", nullable = false) private Integer age; ....

Order.java (modelo)

@Entity @Table(name = "order") @Data @EqualsAndHashCode(exclude = {"id"}) public class Order { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Integer id; @Column(name = "code", nullable = false) private Integer code; @Column(name = "quality", nullable = false) private Integer quality;

...

CustomerRepository.java

public interface CustomerRepository extends JpaRepository<Customer, Integer>{ }

OrderRepository.java

public interface OrderRepository extends JpaRepository<Order, Integer> { }

Finalmente, Application.java

@Configuration @ComponentScan @EnableAutoConfiguration public class Application extends SpringApplication{ public static void main(String[] args) { SpringApplication.run(Application.class, args); } @Bean public ServletRegistrationBean h2Console() { ServletRegistrationBean reg = new ServletRegistrationBean(new WebServlet(), "/console/*"); reg.setLoadOnStartup(1); return reg; } }

Durante el inicio se lanzan las siguientes excepciones :

-10-10 15:45:24.757 ERROR 1549 --- [ main] o.s.boot.SpringApplication : Application startup failed org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ''customerTransactionManager'' defined in class path resource [com/mm/boot/multidb/CustomerConfig.class]: Unsatisfied dependency expressed through constructor argument with index 0 of type [javax.persistence.EntityManagerFactory]: : No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined: expected single matching bean but found 2: customerEntityManager,orderEntityManager; 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: customerEntityManager,orderEntityManager at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:747) at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:462) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1095) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:990) 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:706) at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:762) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482) at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:109) 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.mm.boot.multidb.Application.main(Application.java:17) Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined: expected single matching bean but found 2: customerEntityManager,orderEntityManager at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:974) at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:862) at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:811) at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:739) ... 18 common frames omitted Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ''customerTransactionManager'' defined in class path resource [com/mm/boot/multidb/CustomerConfig.class]: Unsatisfied dependency expressed through constructor argument with index 0 of type [javax.persistence.EntityManagerFactory]: : No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined: expected single matching bean but found 2: customerEntityManager,orderEntityManager; 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: customerEntityManager,orderEntityManager at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:747) at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:462) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1095) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:990) 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:706) at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:762) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482) at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:109) 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.mm.boot.multidb.Application.main(Application.java:17) Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined: expected single matching bean but found 2: customerEntityManager,orderEntityManager at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:974) at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:862) at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:811) at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:739) ... 18 more

El código completo de la muestra se puede encontrar en GitHub ( https://github.com/tonym2105/samples/tree/master/boot-multidb-sample )

Gracias por adelantado por la ayuda.


Aquí está mi solución. base en spring-boot.1.2.5.RELEASE.

application.properties

first.datasource.driver-class-name=com.mysql.jdbc.Driver first.datasource.url=jdbc:mysql://127.0.0.1:3306/test first.datasource.username= first.datasource.password= first.datasource.validation-query=select 1 second.datasource.driver-class-name=com.mysql.jdbc.Driver second.datasource.url=jdbc:mysql://127.0.0.1:3306/test2 second.datasource.username= second.datasource.password= second.datasource.validation-query=select 1

DataSourceConfig.java

@Configuration public class DataSourceConfig { @Bean @Primary @ConfigurationProperties(prefix="first.datasource") public DataSource firstDataSource() { DataSource ds = DataSourceBuilder.create().build(); return ds; } @Bean @ConfigurationProperties(prefix="second.datasource") public DataSource secondDataSource() { DataSource ds = DataSourceBuilder.create().build(); return ds; } }


Gracias a las respuestas de Steve Park y Rafal Borowiec, conseguí que mi código funcionara, sin embargo, tuve un problema: DriverManagerDataSource es una implementación "simple" y NO le proporciona un ConnectionPool (consulte http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/jdbc/datasource/DriverManagerDataSource.html ).

Por lo tanto, reemplacé las funciones que devuelve el DataSource para el secondDB .

public DataSource <secondaryDB>DataSource() { // use DataSourceBuilder and NOT DriverManagerDataSource // as this would NOT give you ConnectionPool DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create(); dataSourceBuilder.url(databaseUrl); dataSourceBuilder.username(username); dataSourceBuilder.password(password); dataSourceBuilder.driverClassName(driverClassName); return dataSourceBuilder.build(); }

Además, si no necesita el EntityManager como tal, puede eliminar tanto la entityManager() como la anotación @Bean .

Además, es posible que desee eliminar la anotación basePackages de su clase de configuración: mantenerla con la llamada factoryBean.setPackagesToScan() es suficiente.


Hay otra forma de tener múltiples DataSources utilizando @EnableAutoConfiguration y application.properties.

Básicamente, coloque información de configuración de múltiples fuentes de datos en application.properties y genere la configuración predeterminada (dataSource y entityManagerFactory) automáticamente para la primera fuente de datos por @EnableAutoConfiguration. Pero para el siguiente dataSource, cree dataSource, entityManagerFactory y transactionManager, todo manualmente mediante la información del archivo de propiedades.

A continuación se muestra mi ejemplo para configurar dos fuentes de datos. El primer dataSource es configurado por @EnableAutoConfiguration, que puede asignarse solo para una configuración, no para múltiples. Y eso generará ''transactionManager'' por DataSourceTransactionManager , que se ve por defecto en el transacciónManager generado por la anotación. Sin embargo, he visto que la transacción no comienza a emitirse en el subproceso del grupo de subprocesos programado solo para el DataSourceTransactionManager predeterminado y también cuando hay varios administradores de transacciones. Así que creo transacciónManager manualmente por JpaTransactionManager también para la primera fuente de datos con la asignación del nombre de bean ''transacciónManager'' y la entidad predeterminadaManagerFactory. Ese JpaTransactionManager para el primer dataSource seguramente resuelve el extraño problema de transacción en el hilo de ScheduledThreadPool.

Actualización para Spring Boot 1.3.0.

Encontré que mi configuración anterior con @EnableAutoConfiguration para la fuente de datos predeterminada tiene un problema al encontrar entityManagerFactory con la versión Spring Boot 1.3. Tal vez la entidad por defecto ManagerManagerFactory no es generada por @EnableAutoConfiguration, una vez que introduzco mi propio transactionManager. Así que ahora creo entityManagerFactory por mí mismo. Así que no necesito usar @EntityScan. Así que parece que estoy obteniendo más y más de la configuración de @EnableAutoConfiguration.

El segundo dataSource se configura sin @EnableAutoConfiguration y crea ''anotherTransactionManager'' de forma manual.

Debido a que hay varias transacciones deManager que se extienden desde PlatformTransactionManager, debemos especificar qué transacciónManager usar en cada anotación @Transactional

Configuración predeterminada del repositorio

@Configuration @EnableTransactionManagement @EnableAutoConfiguration @EnableJpaRepositories( entityManagerFactoryRef = "entityManagerFactory", transactionManagerRef = "transactionManager", basePackages = {"com.mysource.repository"}) public class RepositoryConfig { @Autowired JpaVendorAdapter jpaVendorAdapter; @Autowired DataSource dataSource; @Bean(name = "entityManager") public EntityManager entityManager() { return entityManagerFactory().createEntityManager(); } @Primary @Bean(name = "entityManagerFactory") public EntityManagerFactory entityManagerFactory() { LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean(); emf.setDataSource(dataSource); emf.setJpaVendorAdapter(jpaVendorAdapter); emf.setPackagesToScan("com.mysource.model"); emf.setPersistenceUnitName("default"); // <- giving ''default'' as name emf.afterPropertiesSet(); return emf.getObject(); } @Bean(name = "transactionManager") public PlatformTransactionManager transactionManager() { JpaTransactionManager tm = new JpaTransactionManager(); tm.setEntityManagerFactory(entityManagerFactory()); return tm; } }

Otra configuración de repositorio

@Configuration @EnableTransactionManagement @EnableJpaRepositories( entityManagerFactoryRef = "anotherEntityManagerFactory", transactionManagerRef = "anotherTransactionManager", basePackages = {"com.mysource.anothersource.repository"}) public class AnotherRepositoryConfig { @Autowired JpaVendorAdapter jpaVendorAdapter; @Value("${another.datasource.url}") private String databaseUrl; @Value("${another.datasource.username}") private String username; @Value("${another.datasource.password}") private String password; @Value("${another.dataource.driverClassName}") private String driverClassName; @Value("${another.datasource.hibernate.dialect}") private String dialect; public DataSource dataSource() { DriverManagerDataSource dataSource = new DriverManagerDataSource(databaseUrl, username, password); dataSource.setDriverClassName(driverClassName); return dataSource; } @Bean(name = "anotherEntityManager") public EntityManager entityManager() { return entityManagerFactory().createEntityManager(); } @Bean(name = "anotherEntityManagerFactory") public EntityManagerFactory entityManagerFactory() { Properties properties = new Properties(); properties.setProperty("hibernate.dialect", dialect); LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean(); emf.setDataSource(dataSource()); emf.setJpaVendorAdapter(jpaVendorAdapter); emf.setPackagesToScan("com.mysource.anothersource.model"); // <- package for entities emf.setPersistenceUnitName("anotherPersistenceUnit"); emf.setJpaProperties(properties); emf.afterPropertiesSet(); return emf.getObject(); } @Bean(name = "anotherTransactionManager") public PlatformTransactionManager transactionManager() { return new JpaTransactionManager(entityManagerFactory()); } }

application.properties

# database configuration spring.datasource.url=jdbc:h2:file:~/main-source;AUTO_SERVER=TRUE spring.datasource.username=sa spring.datasource.password= spring.datasource.driver-class-name=org.h2.Driver spring.datasource.continueOnError=true spring.datasource.initialize=false # another database configuration another.datasource.url=jdbc:sqlserver://localhost:1433;DatabaseName=another; another.datasource.username=username another.datasource.password= another.datasource.hibernate.dialect=org.hibernate.dialect.SQLServer2008Dialect another.datasource.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver

Elija el administrador de transacciones adecuado para la anotación @Transactional

Servicio para el primer origen de datos

@Service("mainService") @Transactional("transactionManager") public class DefaultDataSourceServiceImpl implements DefaultDataSourceService { // }

Servicio para otra fuente de datos

@Service("anotherService") @Transactional("anotherTransactionManager") public class AnotherDataSourceServiceImpl implements AnotherDataSourceService { // }


He escrito un artículo completo en Spring Boot JPA Multiple Data Sources Example . En este artículo, aprenderemos cómo configurar múltiples fuentes de datos y conectarnos a múltiples bases de datos en una aplicación web típica de Spring Boot. Utilizaremos Spring Boot 2.0.5, JPA, Hibernate 5, Thymeleaf y la base de datos H2 para construir una aplicación web simple de múltiples fuentes de datos Spring Boot.


No sé por qué, pero funciona. Dos configuraciones son iguales, solo cambie xxx a su nombre.

@Configuration @EnableTransactionManagement @EnableJpaRepositories( entityManagerFactoryRef = "xxxEntityManager", transactionManagerRef = "xxxTransactionManager", basePackages = {"aaa.xxx"}) public class RepositoryConfig { @Autowired private Environment env; @Bean @Primary @ConfigurationProperties(prefix="datasource.xxx") public DataSource xxxDataSource() { return DataSourceBuilder.create().build(); } @Bean public LocalContainerEntityManagerFactoryBean xxxEntityManager() { LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); em.setDataSource(xxxDataSource()); em.setPackagesToScan(new String[] {"aaa.xxx"}); HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); em.setJpaVendorAdapter(vendorAdapter); HashMap<String, Object> properties = new HashMap<String, Object>(); properties.put("hibernate.show_sql", env.getProperty("hibernate.show_sql")); properties.put("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto")); properties.put("hibernate.dialect", env.getProperty("hibernate.dialect")); em.setJpaPropertyMap(properties); return em; } @Bean(name = "xxxTransactionManager") public PlatformTransactionManager xxxTransactionManager() { JpaTransactionManager tm = new JpaTransactionManager(); tm.setEntityManagerFactory(xxxEntityManager().getObject()); return tm; }

}


Verifiqué el código fuente que proporcionaste en GitHub. Hubo varios errores / errores tipográficos en la configuración.

En CustomerDbConfig / OrderDbConfig, debe consultar customerEntityManager y los paquetes deben apuntar a los paquetes existentes:

@Configuration @EnableJpaRepositories( entityManagerFactoryRef = "customerEntityManager", transactionManagerRef = "customerTransactionManager", basePackages = {"com.mm.boot.multidb.repository.customer"}) public class CustomerDbConfig {

Los paquetes para escanear en customerEntityManager y orderEntityManager no apuntaban al paquete adecuado:

em.setPackagesToScan("com.mm.boot.multidb.model.customer");

Además, la inyección de EntityManagerFactory adecuada no funcionó. Debería ser:

@Bean(name = "customerTransactionManager") public PlatformTransactionManager transactionManager(EntityManagerFactory customerEntityManager){ }

Lo anterior estaba causando el problema y la excepción. Al proporcionar el nombre en un método @Bean, está seguro de que se inyecta el EMF adecuado.

Lo último que hice fue deshabilitar la configuración automática de JpaRepositories:

@EnableAutoConfiguration(exclude = JpaRepositoriesAutoConfiguration.class)

¡Y con todas las correcciones, la aplicación comienza como probablemente esperabas!