springboot读写分离
1.配置数据源
spring.datasource.master.username=root
spring.datasource.master.password= 123456
spring.datasource.master.jdbcUrl= jdbc:mysql://192.168.0.155:3306/testdb
spring.datasource.master.driver-class-name= com.mysql.cj.jdbc.Driver
spring.datasource.slave1.username=root
spring.datasource.slave1.password=1qaz!QAZ2wsx
spring.datasource.slave1.jdbcUrl= jdbc:mysql:///192.168.0.155:3306/firstdb
spring.datasource.slave1.driver-class-name= com.mysql.cj.jdbc.Driver
2.读取配置文件
@Configuration
@PropertySource(value= {"file:./config/db.properties", "classpath:db.properties"},ignoreResourceNotFound = true)
@ConfigurationProperties(prefix = "db")
@Data
public class DBConfiguration {
private String username;
private String password;
private String url;
private String driverClassName;
@Bean("db1")
public DataSource getDataSource() {
HikariDataSource hikariDataSource = new HikariDataSource();
hikariDataSource.setDriverClassName(this.driverClassName);
hikariDataSource.setJdbcUrl(this.url);
hikariDataSource.setUsername(this.username);
hikariDataSource.setPassword(this.password);
return hikariDataSource;
}
}
- 多数据源配置
@Configuration
public class DataSourceConfig {
@Bean
public DataSource myRoutingDataSource(@Qualifier("db1") DataSource masterDataSource,
@Qualifier("db2") DataSource slave1DataSource,
@Qualifier("db3") 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.MyBatis配置
@EnableTransactionManagement
@Configuration
public class MyBatisConfig {
@Resource(name = "myRoutingDataSource")
private DataSource myRoutingDataSource;
/**
* 扫描mybatis下的xml文件
* @return
* @throws Exception
*/
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(myRoutingDataSource);
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mybatis/*.xml"));
return sqlSessionFactoryBean.getObject();
}
@Bean
public PlatformTransactionManager platformTransactionManager() {
return new DataSourceTransactionManager(myRoutingDataSource);//因为Spring容器中如今有4个数据源,因此咱们须要为事务管理器和MyBatis手动指定一个明确的数据源。
}
}
5.定义一个枚举来表明这三个数据源
public enum DBTypeEnum {
MASTER,SLAVE1,SLAVE2
}
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.获取路由key
public class MyRoutingDataSource extends AbstractRoutingDataSource {
@Nullable
@Override
protected Object determineCurrentLookupKey() {
return DBContextHolder.get();
}
}
8.全部的查询都走从库,插入/修改/删除走主库。咱们经过方法名来区分操做类型(CRUD)
@Aspect
@Component
public class DataSourceAop {
/**
* 只读:
* 不是Master注解的对象或方法 && select开头的方法 || get开头的方法
*/
@Pointcut("!@annotation(com.microservice.readwriteseparat.annotation.Master) " +
"&& (execution(* com.microservice.readwriteseparat.service..*.select*(..)) " +
"|| execution(* com.microservice.readwriteseparat.service..*.get*(..)))")
public void readPointcut() {
}
/**
* 写:
* Master注解的对象或方法 || insert开头的方法 || add开头的方法 || update开头的方法
* || edlt开头的方法 || delete开头的方法 || remove开头的方法
*/
@Pointcut("@annotation(com.microservice.readwriteseparat.annotation.Master) " +
"|| execution(* com.microservice.readwriteseparat.service..*.insert*(..)) " +
"|| execution(* com.microservice.readwriteseparat.service..*.add*(..)) " +
"|| execution(* com.microservice.readwriteseparat.service..*.update*(..)) " +
"|| execution(* com.microservice.readwriteseparat.service..*.edit*(..)) " +
"|| execution(* com.microservice.readwriteseparat.service..*.delete*(..)) " +
"|| execution(* com.microservice.readwriteseparat..*.remove*(..))")
public void writePointcut() {
}
@Before("readPointcut()")
public void read() {
DBContextHolder.slave();
}
@Before("writePointcut()")
public void write() {
DBContextHolder.master();
}
}
9.定义一个注解 标记使用主库
public @interface Master {
}