问题描述
我想将Spring Boot配置为使用2个JNDI数据源.我尝试了这种配置:
I want to configure Spring Boot to use 2 JNDI datasources. I tried this configuration:
application.properties
spring.production.datasource.jndi-name=java:/global/production_gateway
spring.production.datasource.driver-class-name=org.mariadb.jdbc.Driver
spring.production.datasource.jpa.properties.hibernate.dialect=org.hibernate.dialect.MariaDBDialect
spring.production.datasource.jpa.show-sql = true
spring.production.datasource.jpa.hibernate.ddl-auto = update
spring.warehouse.datasource.jndi-name=java:/global/production_warehouse
spring.warehouse.datasource.driver-class-name=org.mariadb.jdbc.Driver
spring.warehouse.datasource.jpa.properties.hibernate.dialect=org.hibernate.dialect.MariaDBDialect
spring.warehouse.datasource.jpa.show-sql = true
spring.warehouse.datasource.jpa.hibernate.ddl-auto = update
主数据库
primary database
@Configuration
@EnableJpaRepositories(
basePackages = "org.datalis.plugin.production.entity",
entityManagerFactoryRef = "productionEntityManagerFactory",
transactionManagerRef = "productionTransactionManager"
)
@EnableTransactionManagement
public class ContextProductionDatasource {
@Primary
@Bean(name = "productionDataSourceProperties")
@ConfigurationProperties(prefix="spring.production.datasource")
public JndiPropertyHolder productionDataSourceProperties() {
return new JndiPropertyHolder();
}
@Primary
@Bean(name = "productionDataSource")
@ConfigurationProperties(prefix="spring.production.datasource")
public DataSource productionDataSource() {
JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
DataSource dataSource = dataSourceLookup.getDataSource(productionDataSourceProperties().getJndiName());
return dataSource;
}
@Primary
@Bean(name = "productionEntityManager")
public EntityManager productionEntityManager(EntityManagerFactory emf) {
return emf.createEntityManager();
}
@Primary
@Bean(name = "productionEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean productionEntityManagerFactory(
EntityManagerFactoryBuilder builder) {
Map<String, Object> properties = new HashMap<String, Object>();
properties.put("hibernate.hbm2ddl.auto", "update");
return builder
.dataSource(productionDataSource())
.packages("org.datalis.plugin.production.entity")
.persistenceUnit("production")
.properties(properties)
.build();
}
@Primary
@Bean(name = "productionTransactionManager")
public PlatformTransactionManager productionTransactionManager(final EntityManagerFactory emf) {
final JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
@Primary
@Bean(name = "productionExceptionTranslation")
public PersistenceExceptionTranslationPostProcessor productionExceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
private static class JndiPropertyHolder {
private String jndiName;
public String getJndiName() {
return jndiName;
}
public void setJndiName(String jndiName) {
this.jndiName = jndiName;
}
}
}
第二个数据源:
@Configuration
@EnableJpaRepositories(
basePackages = "org.datalis.plugin.warehouse.entity",
entityManagerFactoryRef = "warehouseEntityManagerFactory",
transactionManagerRef = "warehouseTransactionManager"
)
@EnableTransactionManagement
public class ContextWarehouseDatasource {
@Bean(name = "warehouseDataSourceProperties")
@ConfigurationProperties(prefix="spring.warehouse.datasource")
public JndiPropertyHolder warehouseDataSourceProperties() {
return new JndiPropertyHolder();
}
@Bean(name = "warehouseDataSource")
@ConfigurationProperties(prefix="spring.warehouse.datasource")
public DataSource warehouseDataSource() {
JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
DataSource dataSource = dataSourceLookup.getDataSource(warehouseDataSourceProperties().getJndiName());
return dataSource;
}
@Bean(name = "warehouseEntityManager")
public EntityManager warehouseEntityManager(EntityManagerFactory emf) {
return emf.createEntityManager();
}
@Bean(name = "warehouseEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean warehouseEntityManagerFactory(
EntityManagerFactoryBuilder builder) {
Map<String, Object> properties = new HashMap<String, Object>();
properties.put("hibernate.hbm2ddl.auto", "update");
return builder
.dataSource(warehouseDataSource())
.packages("org.datalis.plugin.warehouse.entity")
.persistenceUnit("warehouse")
.properties(properties)
.build();
}
@Bean(name = "warehouseTransactionManager")
public PlatformTransactionManager warehouseTransactionManager(final EntityManagerFactory emf) {
final JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
@Bean(name = "warehouseExceptionTranslation")
public PersistenceExceptionTranslationPostProcessor warehouseExceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
private static class JndiPropertyHolder {
private String jndiName;
public String getJndiName() {
return jndiName;
}
public void setJndiName(String jndiName) {
this.jndiName = jndiName;
}
}
}
部署代码时出现异常:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'entityManagerFactory' available
完整错误堆栈: https://pastebin.com/bBZPZGfu
您知道我该如何解决这个问题?
Do you know how I can solve this issue?
当我移除时:
@Primary
@Bean(name = "productionEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean productionEntityManagerFactory(
EntityManagerFactoryBuilder builder) {
Map<String, Object> properties = new HashMap<String, Object>();
properties.put("hibernate.hbm2ddl.auto", "update");
return builder
.dataSource(productionDataSource())
.packages("org.datalis.plugin.production.entity")
.persistenceUnit("production")
.properties(properties)
.build();
}
该软件包已正确部署.知道为什么吗?
The package is properly deployed. Any idea why?
推荐答案
主要问题是要有2个不同的实体管理器,它们可以访问不同的数据库.
The main problem is to have 2 different Entity managers, which access different databases.
因此是异常的原因:Spring Data JPA尝试创建一组存储库,但不知道要使用哪个实体管理器工厂.默认情况下,Spring Data JPA期望仅一个实体管理器工厂bean,最好命名为entityManagerFactory
,但是您没有这样的实体.
So the cause of the exception: Spring Data JPA tries to create a set of repositories but does not know which entity manager factory to use. By default, Spring Data JPA expects only one Entity manager factory bean, preferably named entityManagerFactory
, but you do not have such.
因此,您必须非常精确地进行配置:例如,您可以将代码组织为两个包:...warehouse.*
和app.production.*
,然后可以指定Spring Data JPA的精确配置:@EnableJpaRepositories(basePackages = "...warehouse.**", entityManagerFactoryRef = "warehouseEntityManagerFactory")
和生产@EnableJpaRepositories(basePackages = "...production.**", entityManagerFactoryRef = "productionEntityManagerFactory")
.
So you have to be really precise in configuration: for example, you can organize your code in 2 packages: ...warehouse.*
and app.production.*
, then you can specify a precise configuration of Spring Data JPA: @EnableJpaRepositories(basePackages = "...warehouse.**", entityManagerFactoryRef = "warehouseEntityManagerFactory")
and for production @EnableJpaRepositories(basePackages = "...production.**", entityManagerFactoryRef = "productionEntityManagerFactory")
.
第二步是确保不执行默认的Data JPA实例化:添加配置属性spring.data.jpa.repositories.enabled=false
将解决此问题.
The second step is to ensure that no default Data JPA instantiation is done: adding the configuration property spring.data.jpa.repositories.enabled=false
will resolve this.
并仔细查看配置,请禁用除精确配置上定义的其他任何@EnableJpaRepositories
或@EntityScan
.
And looking through the configuration disable any kind of other @EnableJpaRepositories
or @EntityScan
except defined above precise configurations.
并且在创建LocalContainerEntityManagerFactoryBean
时不要使用注入的EntityManagerFactoryBuilder
:死掉的简单new LocalContainerEntityManagerFactoryBean()
会更好.
And during creation of LocalContainerEntityManagerFactoryBean
do not use injected EntityManagerFactoryBuilder
: dead simple new LocalContainerEntityManagerFactoryBean()
will work better.
最后但并非最不重要的一点是,它通常与应用程序相关:您必须考虑两阶段提交事务:您有2个数据源,可以在单个事务中访问这些数据源,但是每个数据源都由不同的事务管理器管理.
And last but not least, related to application in general: you have to think about 2-phase commit transactions: you have 2 data sources, which can be accessed within single transactions but each of them is managed by different transaction managers.
这篇关于原因:org.springframework.beans.factory.NoSuchBeanDefinitionException:没有名为'entityManagerFactory'的bean可用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!