【发布时间】:2018-08-28 09:23:03
【问题描述】:
我想用两个不同的数据库节点配置@transactional 注解。
如果一个数据库节点出现故障,那么事务管理器应该与第二个节点一起工作而不会出现任何故障。
如果数据库节点在事务之间失败,那么它也应该与第二个节点一起工作。有什么方法可以在 spring 中配置它
【问题讨论】:
标签: spring spring-boot transactions spring-transactions
我想用两个不同的数据库节点配置@transactional 注解。
如果一个数据库节点出现故障,那么事务管理器应该与第二个节点一起工作而不会出现任何故障。
如果数据库节点在事务之间失败,那么它也应该与第二个节点一起工作。有什么方法可以在 spring 中配置它
【问题讨论】:
标签: spring spring-boot transactions spring-transactions
您可以根据自己的情况调整多租户方法
见https://dzone.com/articles/spring-boot-hibernate-multitenancy-implementation
您需要重写 MultiTenantConnectionProviderImpl 方法
public Connection getConnection(String tenantIdentifier)
public Connection getAnyConnection()
如果第一个失败,则检查并返回第二个 DataSource 连接。
虽然不是一个简单的任务。
【讨论】:
虽然上面已经给出了答案,但我再次发布它并提供更多详细信息,
我同意您可以将MultiTenantConnectionProviderImpl 与CurrentTenantIdentifierResolver 一起使用,我早就实现了这两个功能,并且我正在发布我的代码以获得更多帮助。
首先要了解两者的目的。
CurrentTenantIdentifierResolver 用于识别要用于给定租户的连接,为此您需要租户标识。对于租户识别的逻辑,您可以覆盖它。在您的情况下,它将被修复,即您将始终指向您的第一个数据库。
public class TenantIdentifierAndSchemaResolver implements CurrentTenantIdentifierResolver {
private String tenantIdentifier = "FIRST_DATABASE";
@Override
public String resolveCurrentTenantIdentifier() {
return this.tenantIdentifier;
}
/**
* Reason why it retuns a false is because we don't want to validate the
* tenant identifier in the current session.
*
* Please read below.
* {@link CurrentTenantIdentifierResolver#validateExistingCurrentSessions()}
*/
@Override
public boolean validateExistingCurrentSessions() {
return false;
}
public void setTenantIdentifier(String tenantIdentifier) {
this.tenantIdentifier = tenantIdentifier;
}
}
在此之后,您需要实现MultiTenantConnectionProvider,使用它您将实际切换数据库以接收传入请求。
public class CustomMultiTenantConnectionProvider implements MultiTenantConnectionProvider {
private static final long serialVersionUID = 9033113494774715973L;
private Logger LOG = LogManager.getLogger(CustomMultiTenantConnectionProvider.class);
private BasicDataSource dataSource;
public void setDataSource(BasicDataSource dataSource) {
this.dataSource = dataSource;
}
@Override
public Connection getAnyConnection() throws SQLException {
if (LOG.isDebugEnabled()) {
LOG.debug("Fetching any connection >");
}
return dataSource.getConnection();
}
@Override
public Connection getConnection(String tenantIdentifier) throws SQLException {
Connection tenantSpecificConnection = dataSource.getConnection();
if (!StringUtils.isEmpty(tenantIdentifier)) {
Statement statement = tenantSpecificConnection.createStatement();
statement.executeQuery("use " + tenantIdentifier);
statement.close();
tenantSpecificConnection.setSchema(tenantIdentifier);
} else {
tenantSpecificConnection.setSchema(Constants.SOMEOTHER_DB);
}
return tenantSpecificConnection;
}
@Override
public void releaseAnyConnection(Connection connection) throws SQLException {
if (LOG.isDebugEnabled()) {
LOG.debug("Releasing connection obtained by : getAnyConnection", connection);
}
connection.close();
}
@Override
public void releaseConnection(String tenantIdentifier, Connection connection) throws SQLException {
if (LOG.isDebugEnabled()) {
LOG.debug("Releasing connection for : {}", tenantIdentifier);
}
connection.close();
}
/**
* does the connection provider has support for releasing connection and get
* back the connection as and when needed ?
*
* @return
*/
@Override
public boolean supportsAggressiveRelease() {
return false;
}
@SuppressWarnings("rawtypes")
@Override
public boolean isUnwrappableAs(Class arg0) {
return false;
}
@Override
public <T> T unwrap(Class<T> arg0) {
return null;
}
}
您需要在 spring 配置中声明这些 bean,并在 hibernate 配置中指定。
<bean id="sessionFactory"
class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="org.opensource.example" />
<property name="hibernateProperties">
<map>
<entry key="hibernate.hbm2ddl.auto" value="update"></entry>
<entry key="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect"></entry>
<entry key="hibernate.connection.charSet" value="UTF-8"></entry>
<entry key="hibernate.multiTenancy" value="SCHEMA"></entry>
<entry key="hibernate.tenant_identifier_resolver" value-ref="multitenantSchemaResolverAndTenantIdentifier"></entry>
<entry key="hibernate.multi_tenant_connection_provider"
value-ref="multitenantConnectionProvider"></entry>
<entry key="hibernate.show_sql" value="true"></entry>
<entry key="hibernate.use_sql_comments" value="true"></entry>
<entry key="hibernate.connection.charSet" value="UTF-8"></entry>
</map>
</property>
</bean>
我希望这会有所帮助。
【讨论】: