1、应用层解决:
(1)导入spring-boot-starter-aop包;
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
(2)配置datasource.yml文件,配置主从库的驱动、账号、密码、url等,datasource.yml:
spring:
datasource:
master:
jdbc-url: jdbc:mysql://47.99.221.117:3306/lefeng
username: root #主库
password: 1+1j1804
driver-class-name: com.mysql.cj.jdbc.Driver
slave1:
jdbc-url: jdbc:mysql://47.110.60.167:3306/lefeng
username: root # 从库
password: 1+1j1804
driver-class-name: com.mysql.cj.jdbc.Driver
slave2:
jdbc-url: jdbc:mysql://47.110.60.167:3306/lefeng
username: root # 从库
password: 1+1j1804
driver-class-name: com.mysql.cj.jdbc.Driver
(3)设置DataSourceConfig,将主从数据库注入DataSource中;
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties("spring.datasource.master")
public DataSource masterDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.slave1")
public DataSource slave1DataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.slave2")
public DataSource slave2DataSource() {
return DataSourceBuilder.create().build();
}
@Bean
public DataSource myRoutingDataSource(@Qualifier("masterDataSource") DataSource masterDataSource,
@Qualifier("slave1DataSource") DataSource slave1DataSource,
@Qualifier("slave2DataSource") DataSource slave2DataSource) {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DBTypeEnum.MASTER, masterDataSource);
targetDataSources.put(DBTypeEnum.SLAVE1, slave1DataSource);
targetDataSources.put(DBTypeEnum.SLAVE2, slave2DataSource);
MyRoutingDataSource myRoutingDataSource = new MyRoutingDataSource();
myRoutingDataSource.setDefaultTargetDataSource(masterDataSource);
myRoutingDataSource.setTargetDataSources(targetDataSources);
return myRoutingDataSource;
}
}
(4)定义切面,不同的操作切换成不同的数据源;
@Aspect
@Component
public class DataSourceAop {
@Before("execution(* com.lefeng.lf_front.dao.*.*(..))")
public void before(JoinPoint jp) {
String methodName = jp.getSignature().getName();
if (StringUtils.startsWithAny(methodName, "get", "select", "find")) {
DBContextHolder.slave();
}else {
DBContextHolder.master();
}
}
}
(5)通过MyRoutingDataSource获取当前采用的数据源;
public class MyRoutingDataSource extends AbstractRoutingDataSource {
@Nullable
@Override
protected Object determineCurrentLookupKey() {
return DBContextHolder.get();
}
}
(6)通过ThreadLocal保存当前操作的数据源;
public class DBContextHolder {
private static final ThreadLocal<DBTypeEnum> contextHolder = new ThreadLocal<>();
private static final AtomicInteger counter = new AtomicInteger(-1);
public static void set(DBTypeEnum dbType) {
contextHolder.set(dbType);
}
public static DBTypeEnum get() {
return contextHolder.get();
}
public static void master() {
set(DBTypeEnum.MASTER);
System.out.println("切换到master");
}
public static void slave() {
// 轮询
int index = counter.getAndIncrement() % 2;
if (counter.get() > 9999) {
counter.set(-1);
}
if (index == 0) {
set(DBTypeEnum.SLAVE1);
System.out.println("切换到slave1");
}else {
set(DBTypeEnum.SLAVE2);
System.out.println("切换到slave2");
}
}
}
(7)将MyRoutingDataSource注入到MyBatis配置文件中;
@EnableTransactionManagement
@Configuration
public class MyBatisConfig {
@Resource(name = "myRoutingDataSource")
private DataSource myRoutingDataSource;
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(myRoutingDataSource);
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
return sqlSessionFactoryBean.getObject();
}
@Bean
public PlatformTransactionManager platformTransactionManager() {
return new DataSourceTransactionManager(myRoutingDataSource);
}
}
(8)需要设置数据集的主从复制,即在主库中开放一个只读权限的账号,用于在从库中启动主从复制。