首页 > 应用开发 > Java > SpringBoot 项目优雅实现读写分离
来源:京东云开发者
# 主库配置spring.datasource.master.jdbc-url=jdbc:mysql://ip:port/master?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=falsespring.datasource.master.username=masterspring.datasource.master.password=123456spring.datasource.master.driver-class-name=com.mysql.jdbc.Driver# 从库配置spring.datasource.slave.jdbc-url=jdbc:mysql://ip:port/slave?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=falsespring.datasource.slave.username=slavespring.datasource.slave.password=123456spring.datasource.slave.driver-class-name=com.mysql.jdbc.Driver
@Configuration@ConditionalOnProperty("spring.datasource.master.jdbc-url")public class MasterDataSourceConfiguration { @Bean("masterDataSource") @ConfigurationProperties(prefix = "spring.datasource.master") public DataSource masterDataSource() { return DataSourceBuilder.create().build(); }}@Configuration@ConditionalOnProperty("spring.datasource.slave.jdbc-url")public class SlaveDataSourceConfiguration { @Bean("slaveDataSource") @ConfigurationProperties(prefix = "spring.datasource.slave") public DataSource slaveDataSource() { return DataSourceBuilder.create().build(); }}
public enum DataSourceTypeEnum {/*** 主库*/MASTER,/*** 从库*/SLAVE,;}
4jpublic class DynamicRoutingDataSource extends AbstractRoutingDataSource {("${DB_RW_SEPARATE_SWITCH:false}")private boolean dbRwSeparateSwitch;protected Object determineCurrentLookupKey() {if(dbRwSeparateSwitch && DataSourceTypeEnum.SLAVE.equals(DataSourceContextHolder.getDataSourceType())) {log.info("DynamicRoutingDataSource 切换数据源到从库");return DataSourceTypeEnum.SLAVE;}log.info("DynamicRoutingDataSource 切换数据源到主库");// 根据需要指定当前使用的数据源,这里可以使用ThreadLocal或其他方式来决定使用主库还是从库return DataSourceTypeEnum.MASTER;}}
("spring.datasource.master.jdbc-url")public class DynamicDataSourceConfiguration {("dataSource")public DataSource dynamicDataSource(DataSource masterDataSource, DataSource slaveDataSource) {Map<Object, Object> targetDataSources = new HashMap<>();targetDataSources.put(DataSourceTypeEnum.MASTER, masterDataSource);targetDataSources.put(DataSourceTypeEnum.SLAVE, slaveDataSource);DynamicRoutingDataSource dynamicDataSource = new DynamicRoutingDataSource();dynamicDataSource.setTargetDataSources(targetDataSources);dynamicDataSource.setDefaultTargetDataSource(masterDataSource);return dynamicDataSource;}}
public class DataSourceContextHolder {private static final ThreadLocal<DataSourceTypeEnum> contextHolder = new ThreadLocal<>();public static void setDataSourceType(DataSourceTypeEnum dataSourceType) {contextHolder.set(dataSourceType);}public static DataSourceTypeEnum getDataSourceType() {return contextHolder.get();}public static void clearDataSourceType() {contextHolder.remove();}}
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public @interface MasterDataSource {}@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public @interface SlaveDataSource {}
public class DataSourceAspect {("@annotation(xxx.MasterDataSource)")public void setMasterDataSource(JoinPoint joinPoint) {DataSourceContextHolder.setDataSourceType(DataSourceTypeEnum.MASTER);}("@annotation(xxx.SlaveDataSource)")public void setSlaveDataSource(JoinPoint joinPoint) {DataSourceContextHolder.setDataSourceType(DataSourceTypeEnum.SLAVE);}("@annotation(xxx.MasterDataSource) || @annotation(xxx.SlaveDataSource)")public void clearDataSource(JoinPoint joinPoint) {DataSourceContextHolder.clearDataSourceType();}}
public class TestService {private TestDao testDao;public Test test() {return testDao.queryByPrimaryKey(11L);}}
SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
# 主库配置spring.datasource.master.jdbc-url=jdbc:mysql://ip:port/master?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=falsespring.datasource.master.username=masterspring.datasource.master.password=123456spring.datasource.master.driver-class-name=com.mysql.jdbc.Driverspring.datasource.master.type=com.zaxxer.hikari.HikariDataSourcespring.datasource.master.hikari.name=masterspring.datasource.master.hikari.minimum-idle=5spring.datasource.master.hikari.idle-timeout=30spring.datasource.master.hikari.maximum-pool-size=10spring.datasource.master.hikari.auto-commit=truespring.datasource.master.hikari.pool-name=DatebookHikariCPspring.datasource.master.hikari.max-lifetime=1800000spring.datasource.master.hikari.connection-timeout=30000spring.datasource.master.hikari.connection-test-query=SELECT 1# 从库配置spring.datasource.slave.jdbc-url=jdbc:mysql://ip:port/slave?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=falsespring.datasource.slave.username=rootspring.datasource.slave.password=123456spring.datasource.slave.driver-class-name=com.mysql.jdbc.Driverspring.datasource.slave.type=com.zaxxer.hikari.HikariDataSourcespring.datasource.slave.hikari.name=masterspring.datasource.slave.hikari.minimum-idle=5spring.datasource.slave.hikari.idle-timeout=30spring.datasource.slave.hikari.maximum-pool-size=10spring.datasource.slave.hikari.auto-commit=truespring.datasource.slave.hikari.pool-name=DatebookHikariCPspring.datasource.slave.hikari.max-lifetime=1800000spring.datasource.slave.hikari.connection-timeout=30000spring.datasource.slave.hikari.connection-test-query=SELECT 1
@Intercepts({ @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),})@Componentpublic class WriteInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { // 获取 SQL 类型 DataSourceTypeEnum dataSourceType = DataSourceContextHolder.getDataSourceType(); if(DataSourceTypeEnum.SLAVE.equals(dataSourceType)) { DataSourceContextHolder.setDataSourceType(DataSourceTypeEnum.MASTER); } try { // 执行 SQL return invocation.proceed(); } finally { // 恢复数据源 考虑到写入后可能会反查,后续都走主库 // DataSourceContextHolder.setDataSourceType(dataSourceType); } }}来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70024420/viewspace-2996449/ ,如需转载,请注明出处,否则将追究法律责任。