gpt4 book ai didi

java - 在 Hibernate Multi-Tenancy 配置中禁用 Spring 数据源配置

转载 作者:行者123 更新时间:2023-11-30 10:10:47 26 4
gpt4 key购买 nike

我正在使用 Spring 2.x、Spring Data REST、Hibernate 5.x、Mysql 创建服务器 REST 应用程序。

我按照以下指南配置了 Multi-Tenancy :https://dzone.com/articles/spring-boot-hibernate-multitenancy-implementation唯一的区别是我为每个租户使用一个数据库。

我有一个 MultiTenantConnectionProvider 可以创建到数据库的连接,还有一个 TenantIdentifierResolver 可以获取当前租户。

一些相关的代码:

@Component

public class TenantIdentifierResolver implements CurrentTenantIdentifierResolver {

@Override

public String resolveCurrentTenantIdentifier() {

String tenantId = TenantContext.getCurrentTenant();

if (tenantId != null) {

return tenantId;

}

return DEFAULT_TENANT_ID;

}

@Override

public boolean validateExistingCurrentSessions() {

return true;

}

}

...

@Component
@Profile("prod")
public class MultiTenantConnectionProviderImpl implements MultiTenantConnectionProvider {
private static final long serialVersionUID = 3193007611085791247L;
private Logger log = LogManager.getLogger();

private Map<String, HikariDataSource> dataSourceMap = new ConcurrentHashMap<String, HikariDataSource>();

@Autowired
private TenantRestClient tenantRestClient;

@Autowired
private PasswordEncrypt passwordEncrypt;

@Override
public void releaseAnyConnection(Connection connection) throws SQLException {
connection.close();
}

@Override
public Connection getAnyConnection() throws SQLException {
Connection connection = getDataSource(TenantIdResolver.TENANT_DEFAULT).getConnection();
return connection;

}

@Override
public Connection getConnection(String tenantId) throws SQLException {
Connection connection = getDataSource(tenantId).getConnection();
return connection;
}

@Override
public void releaseConnection(String tenantId, Connection connection) throws SQLException {
log.info("releaseConnection " + tenantId);
connection.close();
}

@Override
public boolean supportsAggressiveRelease() {
return false;
}

@Override
public boolean isUnwrappableAs(Class unwrapType) {
return false;
}

@Override
public <T> T unwrap(Class<T> unwrapType) {
return null;
}

public HikariDataSource getDataSource(@NotNull String tentantId) throws SQLException {
if (dataSourceMap.containsKey(tentantId)) {
return dataSourceMap.get(tentantId);
} else {
HikariDataSource dataSource = createDataSource(tentantId);
dataSourceMap.put(tentantId, dataSource);
return dataSource;
}
}

public HikariDataSource createDataSource(String tenantId) throws SQLException {
log.info("Create Datasource for tenant {}", tenantId);
try {
Database database = tenantRestClient.getDatabase(tenantId);
DatabaseInstance databaseInstance = tenantRestClient.getDatabaseInstance(tenantId);
if (database != null && databaseInstance != null) {
HikariConfig hikari = new HikariConfig();
String driver = "";
String options = "";
switch (databaseInstance.getType()) {
case MYSQL:
driver = "jdbc:mysql://";
options = "?useLegacyDatetimeCode=false&serverTimezone=UTC&useUnicode=yes&characterEncoding=UTF-8&useSSL=false";
break;

default:
driver = "jdbc:mysql://";
options = "?useLegacyDatetimeCode=false&serverTimezone=UTC&useUnicode=yes&characterEncoding=UTF-8&useSSL=false";
}

hikari.setJdbcUrl(driver + databaseInstance.getHost() + ":" + databaseInstance.getPort() + "/" + database.getName() + options);
hikari.setUsername(database.getUsername());
hikari.setPassword(passwordEncrypt.decryptPassword(database.getPassword()));

// MySQL optimizations, see
// https://github.com/brettwooldridge/HikariCP/wiki/MySQL-Configuration
hikari.addDataSourceProperty("cachePrepStmts", true);
hikari.addDataSourceProperty("prepStmtCacheSize", "250");
hikari.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
hikari.addDataSourceProperty("useServerPrepStmts", "true");
hikari.addDataSourceProperty("useLocalSessionState", "true");
hikari.addDataSourceProperty("useLocalTransactionState", "true");
hikari.addDataSourceProperty("rewriteBatchedStatements", "true");
hikari.addDataSourceProperty("cacheResultSetMetadata", "true");
hikari.addDataSourceProperty("cacheServerConfiguration", "true");
hikari.addDataSourceProperty("elideSetAutoCommits", "true");
hikari.addDataSourceProperty("maintainTimeStats", "false");
hikari.setMinimumIdle(3);
hikari.setMaximumPoolSize(5);

hikari.setIdleTimeout(30000);
hikari.setPoolName("JPAHikari_" + tenantId);
// mysql wait_timeout 600seconds
hikari.setMaxLifetime(580000);
hikari.setLeakDetectionThreshold(60 * 1000);

HikariDataSource dataSource = new HikariDataSource(hikari);


return dataSource;

} else {
throw new SQLException(String.format("DB not found for tenant %s!", tenantId));
}
} catch (Exception e) {
throw new SQLException(e.getMessage());
}
}

}

我还以这种方式配置了 Hibernate:

@Configuration
@Profile("prod")
public class HibernateConfig {

@Autowired
private JpaProperties jpaProperties;

@Bean
public JpaVendorAdapter jpaVendorAdapter() {
return new HibernateJpaVendorAdapter();
}

@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource,
MultiTenantConnectionProvider multiTenantConnectionProviderImpl,
CurrentTenantIdentifierResolver currentTenantIdentifierResolverImpl) {
Map<String, Object> properties = new HashMap<>();
properties.putAll(jpaProperties.getHibernateProperties(new HibernateSettings()));
properties.put(Environment.MULTI_TENANT, MultiTenancyStrategy.DATABASE);
properties.put(Environment.MULTI_TENANT_CONNECTION_PROVIDER, multiTenantConnectionProviderImpl);
properties.put(Environment.MULTI_TENANT_IDENTIFIER_RESOLVER, currentTenantIdentifierResolverImpl);
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource);
em.setPackagesToScan("com.server");
em.setJpaVendorAdapter(jpaVendorAdapter());
em.setJpaPropertyMap(properties);

return em;
}

}

这是我的 application.properties:

spring.datasource.url=jdbc:mysql://url:3306/empty?useLegacyDatetimeCode=false&serverTimezone=UTC&useSSL=false
spring.datasource.username=empty
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.password=empty
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL57Dialect
spring.jpa.hibernate.ddl-auto: validate
spring.jpa.hibernate.naming.physical- strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.jpa.show-sql: false

在应用程序启动期间,我看到 Spring 为我在属性文件中配置的数据库创建了一个连接池。

我想避免这种情况,因为我所有的连接都是由 MultiTenantConnectionProviderImpl 创建的。我想继续在我的 bean 中注入(inject) EntityManagerDatasource 的功能。

我已经看到如何禁用 Spring Boot 数据源配置 here ,但这样做我无法再在我的应用程序中注入(inject)数据源。

对于如何从属性文件中完全删除数据源的定义并通过 MultiTenantConnectionProviderImpl 在应用程序中以编程方式注入(inject)数据源,您有什么建议吗?

最佳答案

以下是有关如何以编程方式创建数据源的完整示例。

只是参数是从属性文件中获取的,而不是在 java 类中进行硬编码。

当您定义多个数据源时,您必须定义一个 @Primary 并且只定义一个,然后您将拥有一个 @Qualifier识别数据源。如何管理它们应该很简单。

@RequiredArgsConstructor
@PropertySource({"classpath:persistence/persistence-primarydb.properties"})
@EnableJpaRepositories(basePackages = "io.vforge.cauldron.repository.primary",
entityManagerFactoryRef = "primaryEntityManagerFactory",
transactionManagerRef = "primaryTransactionManager")
@EnableJpaAuditing
@Configuration
public class CauldronPrimaryDatasource {

private final Environment env;

@Primary
@Bean
public LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory() {
LocalContainerEntityManagerFactoryBean em
= new LocalContainerEntityManagerFactoryBean();
em.setDataSource(primaryDataSource());
em.setPackagesToScan("io.vforge.cauldron.model.primary");

HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
HashMap<String, Object> properties = new HashMap<>();
properties.put("hibernate.hbm2ddl.auto", env.getProperty("primary.hibernate.hbm2ddl.auto"));
properties.put("hibernate.dialect", env.getProperty("primary.hibernate.dialect"));
properties.put("hibernate.show_sql", env.getProperty("primary.hibernate.show_sql"));
em.setJpaPropertyMap(properties);

return em;
}

@Primary
@Bean
public HikariDataSource primaryDataSource() {
final HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl(env.getProperty("primary.datasource.url"));
dataSource.setUsername(env.getProperty("primary.datasource.username"));
dataSource.setPassword(env.getProperty("primary.datasource.password"));
return dataSource;
}

@Primary
@Bean
public PlatformTransactionManager primaryTransactionManager() {
JpaTransactionManager transactionManager= new JpaTransactionManager();
transactionManager.setEntityManagerFactory(
primaryEntityManagerFactory().getObject());
return transactionManager;
}

}

关于java - 在 Hibernate Multi-Tenancy 配置中禁用 Spring 数据源配置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52729175/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com