我一直试图将我的JHipster生成的应用程序转换为多租户应用程序,使用此http://jannatconsulting.com/blog/?p=41博客文章作为基础。
我遇到了二级缓存的问题。 Spring启动似乎可以正确检测和设置:
DatabaseConfiguration.java
@Configuration
@EnableConfigurationProperties(JpaProperties.class)
@EnableJpaRepositories(
entityManagerFactoryRef = "masterEntityManager",
transactionManagerRef = "masterTransactionManager",
basePackages = {"com.quadrimular.nts.helium.repository.master"})
@EnableJpaAuditing(auditorAwareRef = "springSecurityAuditorAware")
@EnableTransactionManagement
@EnableElasticsearchRepositories("com.quadrimular.nts.helium.repository.search")
public class DatabaseConfiguration {
@Inject
private Environment env;
@Autowired(required = false)
private MetricRegistry metricRegistry;
@Inject
private DataSourceProperties datasourceProperties;
@Inject
private JHipsterProperties jhipsterProperties;
@Inject
private JpaProperties jpaProperties;
@Inject
private DataSource dataSource;
@Bean(destroyMethod = "close")
@ConditionalOnExpression("#{!environment.acceptsProfiles('cloud') && !environment.acceptsProfiles('heroku')}")
public DataSource dataSource(DataSourceProperties dataSourceProperties, JHipsterProperties jHipsterProperties) {
log.debug("Configuring Master Datasource");
if (dataSourceProperties.getUrl() == null) {
log.error("Your database connection pool configuration is incorrect! The application" +
" cannot start. Please check your Spring profile, current profiles are: {}",
Arrays.toString(env.getActiveProfiles()));
throw new ApplicationContextException("Database connection pool is not configured correctly");
}
HikariConfig config = new HikariConfig();
config.setDataSourceClassName(dataSourceProperties.getDriverClassName());
config.addDataSourceProperty("url", dataSourceProperties.getUrl());
if (dataSourceProperties.getUsername() != null) {
config.addDataSourceProperty("user", dataSourceProperties.getUsername());
} else {
config.addDataSourceProperty("user", ""); // HikariCP doesn't allow null user
}
if (dataSourceProperties.getPassword() != null) {
config.addDataSourceProperty("password", dataSourceProperties.getPassword());
} else {
config.addDataSourceProperty("password", ""); // HikariCP doesn't allow null password
}
//MySQL optimizations, see https://github.com/brettwooldridge/HikariCP/wiki/MySQL-Configuration
if ("com.mysql.jdbc.jdbc2.optional.MysqlDataSource".equals(dataSourceProperties.getDriverClassName())) {
config.addDataSourceProperty("cachePrepStmts", jHipsterProperties.getDatasource().isCachePrepStmts());
config.addDataSourceProperty("prepStmtCacheSize", jHipsterProperties.getDatasource().getPrepStmtCacheSize());
config.addDataSourceProperty("prepStmtCacheSqlLimit", jHipsterProperties.getDatasource().getPrepStmtCacheSqlLimit());
}
if (metricRegistry != null) {
config.setMetricRegistry(metricRegistry);
}
return new HikariDataSource(config);
}
@Bean(name = "masterEntityManager")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(){
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource(datasourceProperties, jhipsterProperties));
em.setPackagesToScan(new String[]{"com.quadrimular.nts.helium.domain.master"});
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(additionalJpaProperties());
em.setPersistenceUnitName("master");
return em;
}
private Properties additionalJpaProperties() {
Properties properties = new Properties();
for (Map.Entry<String, String> entry : jpaProperties.getHibernateProperties(dataSource).entrySet()) {
properties.setProperty(entry.getKey(), entry.getValue());
}
return properties;
}
@Bean(name = "masterTransactionManager")
public JpaTransactionManager transactionManager(EntityManagerFactory masterEntityManager){
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(masterEntityManager);
return transactionManager;
}
}
当spring尝试配置时:
MultiTenancyJPAConfiguration.java
@Configuration
@EnableConfigurationProperties(JpaProperties.class)
@EnableJpaRepositories(
entityManagerFactoryRef = "tenantEntityManager",
transactionManagerRef = "tenantTransactionManager",
basePackages = {"com.quadrimular.nts.helium.repository.tenant"})
@EnableTransactionManagement
public class MultiTenancyJpaConfiguration {
@Bean(name = "tenantEntityManager")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource,
MultiTenantConnectionProvider connectionProvider,
CurrentTenantIdentifierResolver tenantResolver) {
LocalContainerEntityManagerFactoryBean emfBean = new LocalContainerEntityManagerFactoryBean();
emfBean.setDataSource(dataSource);
emfBean.setPackagesToScan("com.quadrimular.nts.helium.domain.tenant");
emfBean.setJpaVendorAdapter(jpaVendorAdapter());
Map<String, Object> properties = new HashMap<>();
properties.put(org.hibernate.cfg.Environment.MULTI_TENANT, MultiTenancyStrategy.DATABASE);
properties.put(org.hibernate.cfg.Environment.MULTI_TENANT_CONNECTION_PROVIDER, connectionProvider);
properties.put(org.hibernate.cfg.Environment.MULTI_TENANT_IDENTIFIER_RESOLVER, tenantResolver);
properties.put("hibernate.ejb.naming_strategy", "org.hibernate.cfg.ImprovedNamingStrategy");
emfBean.setJpaPropertyMap(properties);
return emfBean;
}
@Bean(name = "tenantTransactionManager")
public JpaTransactionManager transactionManager(EntityManagerFactory tenantEntityManager){
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(tenantEntityManager);
return transactionManager;
}
}
我正在追踪这个:
Caused by: org.hibernate.cache.NoCacheRegionFactoryAvailableException: Second-level cache is used in the application, but property hibernate.cache.region.factory_class is not given; please either disable second level cache or set correct region factory using the hibernate.cache.region.factory_class setting and make sure the second level cache provider (hibernate-infinispan, e.g.) is available on the classpath.
我在application-dev.yml
中定义了所有必需的属性hibernate.cache.use_second_level_cache: true
hibernate.cache.use_query_cache: false
hibernate.generate_statistics: true
hibernate.cache.region.factory_class: org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory
对于我的DatabaseConfiguration.java,它似乎正在被Spring引导正确读取和使用。我无法弄清楚为什么它没有检测到属性文件。如果我尝试通过设置禁用缓存:
hibernate.cache.use_second_level_cache: false
DatabaseConfiguration.java会相应地检测并运行,但MultiTenancyJPAConfiguration.java仍然会抛出相同的异常。
我错过了一些明显的东西吗?
答案 0 :(得分:0)
答案是在实体管理器上实际设置jpa属性值。我不确定我是如何忽视这一点的;我以为他们已经设定了。
首先我注入了主数据源和spring boot提供的jpa属性对象,如果我没弄错的话。
<强> MultiTenancyJPAConfiguration.java 强>
@Inject
private JpaProperties jpaProperties;
@Inject
private DataSource dataSource;
然后我使用 DatabaseConfiguration.java
中使用的相同方法设置值<强> MultiTenancyJPAConfiguration.java 强>
@Bean(name = "tenantEntityManager")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource,
MultiTenantConnectionProvider connectionProvider,
CurrentTenantIdentifierResolver tenantResolver) {
LocalContainerEntityManagerFactoryBean emfBean = new LocalContainerEntityManagerFactoryBean();
emfBean.setDataSource(dataSource);
emfBean.setPackagesToScan("com.quadrimular.nts.helium.domain.tenant");
emfBean.setJpaVendorAdapter(jpaVendorAdapter());
Map<String, Object> properties = new HashMap<>();
properties.put(org.hibernate.cfg.Environment.MULTI_TENANT, MultiTenancyStrategy.DATABASE);
properties.put(org.hibernate.cfg.Environment.MULTI_TENANT_CONNECTION_PROVIDER, connectionProvider);
properties.put(org.hibernate.cfg.Environment.MULTI_TENANT_IDENTIFIER_RESOLVER, tenantResolver);
properties.put("hibernate.ejb.naming_strategy", "org.hibernate.cfg.ImprovedNamingStrategy");
emfBean.setJpaPropertyMap(properties);
emfBean.setJpaProperties(additionalJpaProperties());
return emfBean;
}
private Properties additionalJpaProperties() {
Properties properties = new Properties();
for (Map.Entry<String, String> entry : jpaProperties.getHibernateProperties(dataSource).entrySet()) {
properties.setProperty(entry.getKey(), entry.getValue());
}
return properties;
}
使用additionalJpaProperties()方法获取主数据源的所有hibernate jpa属性。然后我在硬编码后设置hibernate属性映射属性。显然,这不是我打算从.yml文件中设置所有jpa值的最干净的解决方案。