最近项目遇到了同一方法内,主数据库操作数据后,需往其他数据源同步数据的情景,在此记录一下实现过程,也参照了下其他大牛的代码
主要有两种实现方式
- 通过主动方式切换数据源
- 直接获取JdbcTemplate
参考文章:
SpringBoot多数据源切换详解,以及开启事务后数据源切换失败处理
springboot+mybatis解决多数据源切换事务控制不生效的问题
实现流程
一、禁用数据库自动配置
禁用数据库自动配置需在Application类上增加配置,可在@SpringBootApplication
注解后,也可在@EnableAutoConfiguration
注解后配置。
@SpringBootApplication(exclude={
DataSourceAutoConfiguration.class})
或
@EnableAutoConfiguration(exclude={
DataSourceAutoConfiguration.class})
有时也需要屏蔽如下类:
DataSourceTransactionManagerAutoConfiguration.class
JdbcTemplateAutoConfiguration.class
HibernateJpaAutoConfiguration.class
二、主动切换数据源方式
2.1 配置数据源类型
通常采用常量或者枚举类型
public enum DBType {
one("one"),
two("two");
private String value;
DBType(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
2.2 配置数据源切换上下文
public class DBContextHolder {
private static final ThreadLocal contextHolder = new ThreadLocal<>();
/**
* 设置数据源
* @param DBType
*/
public static void setDbType(DBType dbType) {
contextHolder.set(dbType.getValue());
}
/**
* 取得当前数据源
* @return
*/
public static String getDbType() {
return (String) contextHolder.get();
}
/**
* 清除上下文数据
*/
public static void clearDbType() {
contextHolder.remove();
}
}
2.3 配置动态切换数据源类
需要继承AbstractRoutingDataSource
类,并重写determineCurrentLookupKey()
方法,从数据源类型中获取当前线程的数据源类型。
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DBContextHolder.getDbType();
}
}
2.4 重写Transaction
当我们配置了事物管理器和拦截Service中的方法后,每次执行Service中方法前会开启一个事务,并且同时会缓存一些东西:DataSource、SqlSessionFactory、Connection等,所以,我们在外面再怎么设置要求切换数据源也没用,因为Conneciton都是从缓存中拿的,所以我们要想能够顺利的切换数据源,实际就是能够动态的根据DatabaseType获取不同的Connection,并且要求不能影响整个事物的特性。
主要包含两个类:
import com.alibaba.druid.support.logging.Log;
import com.alibaba.druid.support.logging.LogFactory;
import org.apache.ibatis.transaction.Transaction;
import org.springframework.jdbc.CannotGetJdbcConnectionException;
import org.springframework.jdbc.datasource.DataSourceUtils;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import static org.apache.commons.lang3.Validate.notNull;
/**
* <P>多数据源切换,支持事务</P>
*
* @author 高仕立
* @date 2018/2/6 9:09
* @since
*/
public class MultiDataSourceTransaction implements Transaction{
private static final Log LOGGER = LogFactory.getLog(MultiDataSourceTransaction.class);
private final DataSource dataSource;
private Connection mainConnection;
private String mainDatabaseIdentification;
private ConcurrentMap<String, Connection> otherConnectionMap;
private boolean isConnectionTransactional;
private boolean autoCommit;
public MultiDataSourceTransaction(DataSource dataSource) {
notNull(dataSource