Java Spring Boot,具有多个数据源的Spring数据JPA
我正试图用SpringBoot和SpringDataJPA将每个@Repositories连接到不同的数据源。我引用了以下内容作为参考。下面是我试图使用SpringDataJPA实现类似解决方案时使用的代码 CustomerDbConfig.java(第一个数据源连接) CustomerDbConfig.java(第二个数据源) Customer.java(模型) Order.java(型号) CustomerRepository.javaJava Spring Boot,具有多个数据源的Spring数据JPA,java,spring,jpa,spring-data,spring-boot,Java,Spring,Jpa,Spring Data,Spring Boot,我正试图用SpringBoot和SpringDataJPA将每个@Repositories连接到不同的数据源。我引用了以下内容作为参考。下面是我试图使用SpringDataJPA实现类似解决方案时使用的代码 CustomerDbConfig.java(第一个数据源连接) CustomerDbConfig.java(第二个数据源) Customer.java(模型) Order.java(型号) CustomerRepository.java public interface CustomerR
public interface CustomerRepository extends JpaRepository<Customer, Integer>{
}
public interface OrderRepository extends JpaRepository<Order, Integer> {
}
@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
示例的完整代码可以在GitHub()上找到
提前感谢您的帮助。我查看了您在GitHub上提供的源代码。配置中有几个错误/打字错误 在CustomerDbConfig/OrderDbConfig中,您应参考customerEntityManager,包应指向现有包:
@Configuration
@EnableJpaRepositories(
entityManagerFactoryRef = "customerEntityManager",
transactionManagerRef = "customerTransactionManager",
basePackages = {"com.mm.boot.multidb.repository.customer"})
public class CustomerDbConfig {
customerEntityManager和orderEntityManager中要扫描的包都没有指向正确的包:
em.setPackagesToScan("com.mm.boot.multidb.model.customer");
此外,注射适当的EntityManagerFactory也不起作用。应该是:
@Bean(name = "customerTransactionManager")
public PlatformTransactionManager transactionManager(EntityManagerFactory customerEntityManager){
}
以上是导致问题和异常的原因。在@Bean方法中提供名称时,您可以确保注入了正确的EMF
我所做的最后一件事是禁用JpaRepositories的自动配置:
@EnableAutoConfiguration(exclude = JpaRepositoriesAutoConfiguration.class)
在所有修复之后,应用程序将按照您的预期启动 通过使用@EnableAutoConfiguration和application.properties,还有另一种拥有多个数据源的方法 基本上,将多个数据源配置信息放在application.properties上,并通过@EnableAutoConfiguration自动为第一个数据源生成默认设置(dataSource和entityManagerFactory)。但是对于下一个数据源,可以通过属性文件中的信息手动创建dataSource、entityManagerFactory和transactionManager 下面是我设置两个数据源的示例。 第一个数据源由@EnableAutoConfiguration设置,它只能为一个配置而不是多个配置分配。这将通过DataSourceTransactionManager生成“transactionManager”,它看起来是由注释生成的默认transactionManager。但是,我已经看到,只有在默认DataSourceTransactionManager以及存在多个事务管理器的情况下,调度线程池中的线程上出现了事务未开始问题。 因此,我通过JpaTransactionManager手动创建transactionManager,并为第一个数据源分配“transactionManager”bean名称和默认entityManagerFactory。第一个数据源的JpaTransactionManager肯定解决了ScheduledThreadPool线程上的奇怪事务问题 Spring Boot 1.3.0.版本的更新 我发现我以前使用@EnableAutoConfiguration进行的默认数据源配置在使用SpringBoot1.3版本查找entityManagerFactory时出现问题。在我介绍自己的transactionManager之后,@EnableAutoConfiguration可能不会生成默认entityManagerFactory。 所以现在我自己创建entityManagerFactory。所以我不需要使用@EntityScan。因此,通过@EnableAutoConfiguration,我似乎越来越了解设置 第二个数据源是在没有@EnableAutoConfiguration的情况下设置的,并通过手动方式创建“另一个TransactionManager” 因为有多个transactionManager扩展自PlatformTransactionManager,所以我们应该指定在每个@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());
}
}
为@Transactional注释选择适当的transactionManager
第一个数据源的服务
@Service("mainService")
@Transactional("transactionManager")
public class DefaultDataSourceServiceImpl implements DefaultDataSourceService
{
//
}
@Service("anotherService")
@Transactional("anotherTransactionManager")
public class AnotherDataSourceServiceImpl implements AnotherDataSourceService
{
//
}
另一数据源的服务
@Service("mainService")
@Transactional("transactionManager")
public class DefaultDataSourceServiceImpl implements DefaultDataSourceService
{
//
}
@Service("anotherService")
@Transactional("anotherTransactionManager")
public class AnotherDataSourceServiceImpl implements AnotherDataSourceService
{
//
}
这是我的解决办法。基于spring-boot.1.2.5.1版本 应用程序属性 DataSourceConfig.java
不知道为什么,但它是有效的。两种配置相同,只需将xxx更改为您的名称即可
@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;
}
@配置
@启用事务管理
@授权代理(
entityManagerFactoryRef=“xxxEntityManager”,
transactionManagerRef=“xxxTransactionManager”,
basePackages={“aaa.xxx”})
公共类RepositoryConfig{
@自动连线
私人环境署;
@豆子
@初级的
@配置属性(前缀=“datasource.xxx”)
公共数据源xxxDataSource(){
返回DataSourceBuilder.create().build();
}
@豆子
public LocalContainerEntityManagerFactoryBean xxxEntityManager(){
LocalContainerEntityManagerFactoryBean em=新的LocalContainerEntityManagerFactoryBean();
em.setDataSource(xxxDataSource());
em.setPackagesToScan(新字符串[]{“aaa.xxx”});
HibernateJavaEndorapter vendorAdapter=新的HibernateJavaEndorapter();
em.setjpavendor适配器(供应商适配器);
HashMap属性=新建HashMap();
properties.put(“hibernate.show_sql”,env.getProperty(“hibernate.show_sql”);
properties.put(“hibernate.hbm2ddl.auto”,env.getProperty(“hibernate.hbm2ddl.auto”);
properties.put(“hibernate.dial”,env.getProperty(“hibernate.dial”);
em.setJpaPropertyMap(属性);
返回em;
}
@Bean(name=“xxxTransactionManager”)
公共平台TransactionManager xxxTransactionManager(){
JpaTransactionManager tm=新的JpaTransactionManager();
tm.setEntityManager工厂(xxxeEntityManager().getObject());
返回tm;
}
}多亏了Steve Park和Rafal Borowiec的回答,我的代码得以运行,但是,我有一个问题:DriverManager数据源是一个“简单”的实现,它不会给你一个连接池(检查) 因此,我替换了为
secondDB
返回DataSource
的函数
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();
}
公共数据源数据源(){
//使用DataSourceBuilder而不是DriverManager DataSource
//a
@Service("anotherService")
@Transactional("anotherTransactionManager")
public class AnotherDataSourceServiceImpl implements AnotherDataSourceService
{
//
}
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
@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;
}
}
@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;
}
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();
}