我想将Spring Boot配置为使用2个JNDI数据源。我尝试了以下配置:
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
主数据库
@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
你知道我该怎么解决吗?
当我删除时:
@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(); }
该软件包已正确部署。知道为什么吗?
主要问题是要有2个不同的实体管理器,它们可以访问不同的数据库。
因此,产生异常的原因是:Spring Data JPA尝试创建一组存储库,但不知道要使用哪个实体管理器工厂。默认情况下,Spring Data JPA期望只使用一个实体管理器工厂bean(最好命名为)entityManagerFactory,但是您没有这样的名字。
entityManagerFactory
因此,您必须非常精确地进行配置:例如,您可以将代码组织为2个软件包:...warehouse.*和app.production.*,然后可以指定Spring Data JPA:@EnableJpaRepositories(basePackages = "...warehouse.**", entityManagerFactoryRef = "warehouseEntityManagerFactory") 和供生产使用的精确配置@EnableJpaRepositories(basePackages = "...production.**", entityManagerFactoryRef = "productionEntityManagerFactory")。
...warehouse.*
app.production.*
@EnableJpaRepositories(basePackages = "...warehouse.**", entityManagerFactoryRef = "warehouseEntityManagerFactory")
@EnableJpaRepositories(basePackages = "...production.**", entityManagerFactoryRef = "productionEntityManagerFactory")
第二步是确保不执行默认的Data JPA实例化:添加配置属性spring.data.jpa.repositories.enabled=false将解决此问题。
spring.data.jpa.repositories.enabled=false
并通过配置查看禁用任何其他类型@EnableJpaRepositories或@EntityScan以上定义的 精确配置 。
@EnableJpaRepositories
@EntityScan
并且在创建过程中LocalContainerEntityManagerFactoryBean不要使用注入EntityManagerFactoryBuilder:死简单new LocalContainerEntityManagerFactoryBean()将更好地工作。
LocalContainerEntityManagerFactoryBean
EntityManagerFactoryBuilder
new LocalContainerEntityManagerFactoryBean()
最后但并非最不重要的一点是,它通常与应用程序有关:您必须考虑两阶段提交事务:您有2个数据源,可以在单个事务中访问这些数据源,但是每个数据源都由不同的事务管理器管理。