总体步骤:
1.配置多个数据源:分别为主数据源(用于写操作)和从数据源(用于读操作)。
2.实现动态数据源切换:通过 AbstractRoutingDataSource 或者自定义的 RoutingDataSource 类来决定每个请求使用哪个数据源。
3.配置事务管理器:确保事务正确处理。
4.整合 MyBatis:需要配置 SqlSessionFactory 并集成到 Spring 中
下面做个简单示例:
1.配置文件application.yaml新增多数据源配置项
spring:
datasource:
multi-datasource: true
druid:
master:
url: jdbc:mysql://localhost:3306/master_db
username: user
password: pass
driver-class-name: com.mysql.cj.jdbc.Driver
slave:
url: jdbc:mysql://localhost:3306/slave_db
username: user
password: pass
driver-class-name: com.mysql.cj.jdbc.Driver
2.新建config类,配置多数据源
@Configuration
public class DruidDataSourceConfig {
@Autowired
private Environment env;
@Bean(name = "masterDataSource")
public DataSource master() {
return DruidDataSourceBuilder.create()
.configurationProperties(env.getProperty("spring.datasource.druid.master"))
.build();
}
@Bean(name = "slaveDataSource")
public DataSource slave() {
return DruidDataSourceBuilder.create()
.configurationProperties(env.getProperty("spring.datasource.druid.slave"))
.build();
}
@Bean
public DynamicDataSource dataSource(@Qualifier("masterDataSource") DataSource master,
@Qualifier("slaveDataSource") DataSource slave) {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DatabaseType.MASTER, master);
targetDataSources.put(DatabaseType.SLAVE, slave);
DynamicDataSource dataSource = new DynamicDataSource();
dataSource.setTargetDataSources(targetDataSources);
dataSource.setDefaultTargetDataSource(master);
return dataSource;
}
@Bean
public SqlSessionFactory sqlSessionFactory(@Qualifier("dataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);
return factoryBean.getObject();
}
@Bean
public DataSourceTransactionManager transactionManager(@Qualifier("masterDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
3.新建多数据源选择策略
public class DataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
public static void setDataSourceType(String dataSourceType) {
contextHolder.set(dataSourceType);
}
public static String getDataSourceType() {
return contextHolder.get();
}
}
4.关键点:通过org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource.
determineCurrentLookupKey实现动态数据源切换
public class DynamicDataSource extends AbstractRoutingDataSource {
private static final Logger logger = LoggerFactory.getLogger(DynamicDataSource.class);
public static final Map<DatabaseType, List<String>> METHOD_TYPE_MAP = new HashMap<>();
@Nullable
@Override
protected Object determineCurrentLookupKey() {
DatabaseType type = DatabaseContextHolder.getDatabaseType();
return type;
}
public void setMethodType(DatabaseType type, String content) {
List<String> list = Arrays.asList(content.split(","));
METHOD_TYPE_MAP.put(type, list);
}
}
5.在mybatis服务层调用(一般这里可通过切面aspect进行动态切换,根据你的数据源配置的规则, 通过DataSourceContextHolder.setDataSourceType实现动态切换,记得在切面完成后,清除掉数据源)
@Service
public class MyService {
@Autowired
private MyDao myDao;
@Transactional
public void updateSomething() {
DataSourceContextHolder.setDataSourceType(DatabaseType.MASTER.name());
myDao.updateSomething();
}
public List<Something> getSomething() {
DataSourceContextHolder.setDataSourceType(DatabaseType.SLAVE.name());
return myDao.getSomething();
}
}