这是我正在尝试使用Spring Data JPA实现类似解决方案的代码。
CustomerDbConfig.java(第一个数据源连接)
@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(第二个数据源)
@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(模型) ... @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(模型)
@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> { }
最后,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; } }
在启动过程中会引发以下异常:
-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
预先感谢你的帮助。
通过使用@EnableAutoConfiguration和application.properties,还有另一种具有多个数据源的方法。
基本上,将多个dataSource配置信息放在application.properties上,并通过@EnableAutoConfiguration自动为第一个dataSource生成默认设置(dataSource和EntityManagerFactory)。但是对于下一个dataSource,请通过属性文件中的信息手动创建dataSource,entityManagerFactory和transactionManager。
下面是我设置两个数据源的示例。第一个数据源由@EnableAutoConfiguration设置,只能分配给一个配置,不能分配给多个。然后将通过DataSourceTransactionManager生成“ transactionManager” ,该外观看起来是由注释生成的默认transactionManager。但是,我已经看到,仅对于默认的DataSourceTransactionManager以及在有多个事务管理器的情况下,调度线程池中的线程上的事务才不会开始出现问题。所以我通过JpaTransactionManager手动创建transactionManager也为第一个dataSource分配了“ transactionManager” Bean名称和默认的entityManagerFactory。用于第一个数据源的JpaTransactionManager确实可以解决ScheduledThreadPool中线程上的奇怪事务问题。
Spring Boot 1.3.0.RELEASE的更新
我发现我以前使用@EnableAutoConfiguration的默认dataSource配置在使用Spring Boot 1.3版本查找entityManagerFactory时遇到问题。引入我自己的transactionManager之后,也许@EnableAutoConfiguration不会生成默认的entityManagerFactory。所以现在我自己创建了entityManagerFactory。因此,我不需要使用@EntityScan。因此,看起来我通过@EnableAutoConfiguration获得的安装越来越少。
第二个数据源是在没有@EnableAutoConfiguration的情况下设置的,并通过手动方式创建了“ anotherTransactionManager”。
由于从PlatformTransactionManager扩展了多个transactionManager,因此我们应在每个@Transactional注释上指定要使用的transactionManager
默认存储库配置
@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; } }
另一个存储库配置
@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
为@Transactional注释选择适当的transactionManager
第一数据源服务
@Service("mainService") @Transactional("transactionManager") public class DefaultDataSourceServiceImpl implements DefaultDataSourceService { // }
为另一个数据源提供服务
@Service("anotherService") @Transactional("anotherTransactionManager") public class AnotherDataSourceServiceImpl implements AnotherDataSourceService { // }