springboot mysql实现读写分离

本文详细介绍了如何在Spring Boot环境中配置MySQL主从复制,包括POM设置、枚举定义、数据源路由、线程切换、配置文件、切面与注解的使用。通过具体代码示例,展示了如何实现读写分离,提高数据库访问效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

mysql主从设置参考:mysql主从设置

1. pom设置

spring:

    data:
        master:
              driver-class-name: com.mysql.jdbc.Driver
              jdbc-url: jdbc:mysql://192.168.1.47:3306/im-block?autoReconnect=true&useUnicode=true&allowMultiQueries=true&characterEncoding=utf-8&useSSL=false
              username: root
              password: 1204
        slave:
              driver-class-name: com.mysql.jdbc.Driver
              jdbc-url: jdbc:mysql://192.168.1.49:3306/im-block?autoReconnect=true&useUnicode=true&allowMultiQueries=true&characterEncoding=utf-8&useSSL=false
              username: imblock
              password: 1204

2. 定义主从类型:

public enum DBTypeEnum {

    MASTER,

    SLAVE;

}

3. 定义路由数据源的实现类

public class DbDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        System.out.println("DBContextHolder.getDb()=="+DBContextHolder.getDb());
        return DBContextHolder.getDb();
    }

}

4. 定义线程切换

public class DBContextHolder {

    private static final ThreadLocal<DBTypeEnum> contextHolder = new ThreadLocal<>();

    public static void setDb(DBTypeEnum dbType) {
        contextHolder.set(dbType);
    }

    public static DBTypeEnum getDb() {
        return contextHolder.get();
    }

    public static void remove() {
        System.out.println("移除线程="+contextHolder.get());
        System.out.println("移除线程");
        contextHolder.remove();
    }

}

5. 配置数据源数据源和路由配置

@Configuration
public class DbConfig {

    @Bean
    @Primary
    @ConfigurationProperties("spring.datasource.master")
    public DataSource masterDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties("spring.datasource.slave")
    public DataSource slaveDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    public DataSource sqlDataSource(@Qualifier("masterDataSource") DataSource masterDataSource,
                                          @Qualifier("slaveDataSource") DataSource slaveDataSource) {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DBTypeEnum.MASTER, masterDataSource);
        targetDataSources.put(DBTypeEnum.SLAVE, slaveDataSource);
        DbDataSource dbDataSource = new DbDataSource();
        dbDataSource.setDefaultTargetDataSource(masterDataSource);
        dbDataSource.setTargetDataSources(targetDataSources);
        return dbDataSource;
    }

}
@Configuration
@EnableTransactionManagement
public class SqlConfig {

    @Resource(name = "sqlDataSource")
    private DataSource sqlDataSource;

    @Bean
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        sqlSessionFactoryBean.setDataSource(sqlDataSource);
        sqlSessionFactoryBean.setMapperLocations(resolver.getResources("classpath:mapper/*/*.xml"));
        Objects.requireNonNull(sqlSessionFactoryBean.getObject()).getConfiguration().setMapUnderscoreToCamelCase(true);
        return sqlSessionFactoryBean.getObject();
    }

    @Bean
    public PlatformTransactionManager platformTransactionManager() {
        return new DataSourceTransactionManager(sqlDataSource);
    }

}

6.设置切面与注解

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface DataSource {
    DBTypeEnum value() default DBTypeEnum.MASTER;
}
@Aspect
@Component
@Service
@Order(Ordered.HIGHEST_PRECEDENCE)
public class DataSourceAspect {
    protected Logger logger = LoggerFactory.getLogger(getClass());

    @Pointcut("@annotation(com.imblock.common.annotation.DataSource) " +
            "|| @within(com.imblock.common.annotation.DataSource)")
    public void dataSourcePointCut() {
        logger.debug("dataSourcePointCut in");
    }

    @Around("dataSourcePointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Class targetClass = point.getTarget().getClass();
        Method method = signature.getMethod();

        DataSource targetDataSource = (DataSource)targetClass.getAnnotation(DataSource.class);
        DataSource methodDataSource = method.getAnnotation(DataSource.class);
        if(targetDataSource != null || methodDataSource != null){
            DBTypeEnum value;
            if(methodDataSource != null){
                value = methodDataSource.value();
            }else {
                value = targetDataSource.value();
            }

            DBContextHolder.setDb(value);
            logger.debug("set datasource is {}", value);
        }

        try {
            return point.proceed();
        } finally {
            DBContextHolder.remove();
            logger.debug("clean datasource");
        }
    }

7. 在需要切换的方法上加 

@DataSource(DBTypeEnum.SLAVE)

FAQ:注意点:

注解在controller是有效的,但是如果引入了shiro,在service层是无效的,需要在认证的类里面将注入的service类全部延迟启动。不然是无效的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值