自己封装了一个读写分离的 Starter,可以配置任意多个数据源,使用 Hikari 连接池(暂不支持其他连接池)。
GitHub:rw-separate-spring-boot-starter
代码已上传到 Maven 中央仓库,可以直接使用:
<dependency>
<groupId>top.cloudli</groupId>
<artifactId>rw-separate-spring-boot-starter</artifactId>
<version>1.0.0</version>
</dependency>
首先需要排除默认的 DataSourceAutoConfiguration
:
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class FooApplication {
public static void main(String[] args) {
SpringApplication.run(FooApplication.class, args);
}
}
HikariCP 配置
hikari 的配置没有变化,与 Spring Boot 默认的配置方式一样:
spring:
datasource:
hikari:
minimum-idle: 50
maximum-pool-size: 500
connection-test-query: "SELECT 1"
...
application.yml
spring:
separated-datasource:
# 主库数据源
masters:
- {
dataSourceName: master_1,
driverClassName: com.mysql.cj.jdbc.Driver,
url: jdbc:mysql://10.0.0.100:3306/test,
username: root,
password: root
}
# 从库数据源
slaves:
- {
dataSourceName: slave_1,
driverClassName: com.mysql.cj.jdbc.Driver,
url: jdbc:mysql://10.0.0.101:3306/test,
username: reader,
password: reader
}
- {
dataSourceName: slave_2,
driverClassName: com.mysql.cj.jdbc.Driver,
url: jdbc:mysql://10.0.0.102:3306/test,
username: reader,
password: reader
}
dataSourceName
为数据源的名称,请避免名称重复(如果不需要显式地指定数据源,可以不设置)。- 当设置多个数据源时,默认使用轮询的方式来切换数据源。
MyBatis 数据源配置
自动装配的数据源为 routingDataSource
。
@Configuration
@EnableTransactionManagement
public class MyBatisConfig {
// 注入 routingDataSource
@Resource(name = "routingDataSource")
private DataSource dataSource;
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources("classpath:mybatis/mapper/*.xml"));
return sqlSessionFactoryBean.getObject();
}
@Bean
public PlatformTransactionManager platformTransactionManager() {
return new DataSourceTransactionManager(dataSource);
}
}
也可以这样(Bean 的名称必须为 routingDataSource):
private final DataSource routingDataSource;
public MyBatisConfig(DataSource routingDataSource) {
this.routingDataSource = routingDataSource;
}
使用注解实现读写分离
@Service
public class FooService {
@Resource
FooMapper fooMapper;
/**
* 使用读库
*/
@Read
public List<Item> getFoo() {
return fooMapper.getAll();
}
/**
* 使用写库
*/
@Write
public int addFoo(Foo foo) {
return fooMapper.add(foo);
}
}
可以使用参数显式地指定数据源(需要在 application.yml
中指定 dataSourceName
):
@Service
public class FooService {
/**
* 显式地指定数据源为 master_1
*/
@Write("master_1")
public int addFoo(Foo foo) {
return fooMapper.add(foo);
}
}
显式地指定数据源时,@Write
只能指定 masters 中的数据源,@Read
只能指定 slaves 中的数据源。
不要在 DAO 层使用读写分离,无法支持事务。