MyCat实现读写分离与动态数据源切换实现方式之一

本文介绍了如何在代码层面实现MyCat的读写分离,通过AOP切面判断操作类型,动态设置数据源。增删改操作使用updateDataSource,查询操作使用selectDataSource。提供详细的应用配置及关键代码示例,并分享了GitHub项目链接。
摘要由CSDN通过智能技术生成

接上一篇:https://blog.csdn.net/jiayoubing/article/details/106605141

已经通过mycat对数据进行了读写分离,在代码中如何实现读写用不同的DataSource呢?

原理是:通过切面的方式对方法进行判断,如果是增删改,设置数据源为updateDataSource,如果是查询操作,设置数据源为selectDataSource数据源。

代码实现如下:

application.yml

spring:
  datasource:
    ###可读数据源
    select:
      jdbc-url: jdbc:mysql://192.168.234.6:8066/mycat_testdb
      driver-class-name: com.mysql.jdbc.Driver
      username: user
      password: user
    ####可写数据源  
    update:
      jdbc-url: jdbc:mysql://192.168.234.6:8066/mycat_testdb
      driver-class-name: com.mysql.jdbc.Driver
      username: root
      password: 123456
    type: com.alibaba.druid.pool.DruidDataSource

配置2个数据源,DataSourceConfig.java

package com.infosys.config;

import javax.sql.DataSource;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 
* 加载yml配置的数据源
* 
* @author Jiayoubing
* @version [版本号,2020年6月8日]
* @see [相关类/方法]
* @since [产品/模块版本]
 */
@Configuration
public class DataSourceConfig {

	// 创建可读数据源
	@Bean(name = "selectDataSource")
	@ConfigurationProperties(prefix = "spring.datasource.select") // application.properteis中对应属性的前缀
	public DataSource dataSource1() {
		return DataSourceBuilder.create().build();
	}

	// 创建可写数据源
	@Bean(name = "updateDataSource")
	@ConfigurationProperties(prefix = "spring.datasource.update") // application.properteis中对应属性的前缀
	public DataSource dataSource2() {
		return DataSourceBuilder.create().build();
	}

}

DataSourceContextHolder.java

package com.infosys.config;

import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

@Component
@Lazy(false)
public class DataSourceContextHolder {
	// 采用ThreadLocal 保存本地多数据源
	private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();

	// 设置数据源类型
	public static void setDbType(String dbType) {
		contextHolder.set(dbType);
	}

	public static String getDbType() {
		return contextHolder.get();
	}

	public static void clearDbType() {
		contextHolder.remove();
	}

}

DynamicDataSource.java

package com.infosys.config;
// 在Spring 2.0.1中引入了AbstractRoutingDataSource, 该类充当了DataSource的路由中介, 能有在运行时,
// 根据某种key值来动态切换到真正的DataSource上。

import java.util.HashMap;
import java.util.Map;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.stereotype.Component;

@Component
@Primary //对同一个接口,可能会有几种不同的实现类,而默认只会采取其中一种的情况下
public class DynamicDataSource extends AbstractRoutingDataSource
{
    @Autowired
    @Qualifier("selectDataSource")
    private DataSource selectDataSource;

    @Autowired
    @Qualifier("updateDataSource")
    private DataSource updateDataSource;

    /**
     * 这个是主要的方法,返回的是生效的数据源名称
     */
    @Override
    protected Object determineCurrentLookupKey()
    {
        System.out.println("DataSourceContextHolder:::" + DataSourceContextHolder.getDbType());
        return DataSourceContextHolder.getDbType();
    }

    /**
     * 配置数据源信息
     */
    @Override
    public void afterPropertiesSet()
    {
        Map<Object, Object> map = new HashMap<>();
        map.put("selectDataSource", selectDataSource);
        map.put("updateDataSource", updateDataSource);
        setTargetDataSources(map);
        setDefaultTargetDataSource(updateDataSource);//指定一个默认的数据源
        super.afterPropertiesSet();
    }
}

这里是关键,通过AOP来实现动态设置数据源  SwitchDataSourceAOP.java

package com.infosys.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import com.infosys.config.DataSourceContextHolder;

@Aspect
@Component
@Lazy(false)//不使用延迟加载
@Order(0) // Order设定AOP执行顺序 使之在数据库事务上先执行,默认是最低优先级,值越小优先级越高
public class SwitchDataSourceAOP
{
    /**
     * 在执行server方法之前,先根据方法头来判断要做哪种类型的操作,来设置数据源
     * 
     * @param joinPoint[参数说明]
     * @return void[返回类型说明]
     * @exception throws
     *                [违例类型][违例说明]
     * @see [类、类#方法、类#成员]
     */

    @Before("execution(* com.infosys.service.*.*(..))")
    public void process(JoinPoint joinPoint)
    {
        String methodName = joinPoint.getSignature().getName();
        if (methodName.startsWith("get") || methodName.startsWith("count") || methodName.startsWith("find")
                || methodName.startsWith("list") || methodName.startsWith("select") || methodName.startsWith("check"))
        {
            // 读
            DataSourceContextHolder.setDbType("selectDataSource");
        }
        else
        {
            // 切换dataSource
            DataSourceContextHolder.setDbType("updateDataSource");
        }
    }
}

当时insert操作的时候,后台日志显示调用的是updateDataSource

当find操作的时候,后台日志显示调用的是selectDataSource

 

 

github地址:https://github.com/jiayoubing/SpringBootDataSource.git

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值