1.链接数据库yml配置
spring: datasource: master: jdbc-url: jdbc:mysql://localhost:3306/dams?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true username: root password: root driver-class-name: com.mysql.jdbc.Driver slave1: jdbc-url: jdbc:mysql://localhost:3306/root?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true username: root password: root driver-class-name: com.mysql.jdbc.Driver slave2: jdbc-url: jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true username: root password: root driver-class-name: com.mysql.jdbc.Driver
2.数据源配置
/** * * 关于数据源配置,参考SpringBoot官方文档第79章《Data Access》 * 79. Data Access * * 79.1 Configure a Custom DataSource * 79.2 Configure Two DataSources */ @Configuration public class DataSourceConfig { //将master数据源注入 @Bean @Primary @ConfigurationProperties("spring.datasource.master") public DataSource masterDataSource() { return DataSourceBuilder.create().build(); } //将slave1注入 @Bean @ConfigurationProperties("spring.datasource.slave1") public DataSource slave1DataSource() { return DataSourceBuilder.create().build(); } //将slave2注入 @Bean @ConfigurationProperties("spring.datasource.slave2") public DataSource slave2DataSource() { return DataSourceBuilder.create().build(); } // 我们配置了4个数据源,1个master,2两个slave,1个路由数据源。前3个数据源都是为了生成第4个数据源,而且后续我们只用这最后一个路由数据源。 @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; } }
3.SqlSessionFactory
@EnableTransactionManagement @Configuration public class MyBatisConfig { @Resource(name = "myRoutingDataSource") private DataSource myRoutingDataSource; //将数据源设置到SqlSessionFactory @Bean public SqlSessionFactory sqlSessionFactory( MybatisProperties mybatisProperties) throws Exception { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(myRoutingDataSource); sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml")); sqlSessionFactoryBean.setConfiguration(mybatisProperties.getConfiguration()); return sqlSessionFactoryBean.getObject(); } //将数据源设置到事务平台管理器 @Bean public PlatformTransactionManager platformTransactionManager() { return new DataSourceTransactionManager(myRoutingDataSource); } }
4.
public enum DBTypeEnum { MASTER,SLAVE1,SLAVE2; } //定义注解用来强制访问主库 public @interface Master { } //数据库切换 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"); } } }
5.
public class MyRoutingDataSource extends AbstractRoutingDataSource { //根据路由来确定map中的sqlsessionFactory @Nullable @Override protected Object determineCurrentLookupKey() { return DBContextHolder.get(); } }
6.注入多数据源
public class DataSourceAop { @Pointcut("!@annotation(com.tl.base.datasource.annotation.Master) " + "&& (execution(* com.tl.base.domain..*.query*(..)) " + "|| execution(* com.tl.base.domain..*.get*(..)))") public void readPointcut() { } @Pointcut("@annotation(com.tl.base.datasource.annotation.Master) " + "|| execution(* com.tl.base.domain..*.insert*(..)) " + "|| execution(* com.tl.base.domain..*.add*(..)) " + "|| execution(* com.tl.base.domain..*.update*(..)) " + "|| execution(* com.tl.base.domain..*.edit*(..)) " + "|| execution(* com.tl.base.domain..*.delete*(..)) " + "|| execution(* com.tl.base.domain..*.remove*(..))") public void writePointcut() { } @Before("readPointcut()") public void read() { DBContextHolder.slave(); } @Before("writePointcut()") public void write() { DBContextHolder.master(); }