Spring多数据源实现

1、背景

在一个工程中很多情况需要使用多数据源,典型的情况是读写库和写库的地址不一样,需要进行读写库和写库的分离。如下图,直观的方法是配置多个数据源,每个数据源对应一个SqlSession
多数据源原始实现
这样存在的问题是由于每个数据源有自己的一套连接方式,导致代码冗余比较多。多数据源问题,归根结底是连接串的地址不一样(DataSource配置不一样),Spring提供了在DataSource层多数据源切换方式
这里写图片描述

2、Spring多数据源切换实现

Spring提供了AbstractRoutingDataSource类来实现多数据源之间的切换,它里面维护了一个用Map存储的键值对,其中键为DataSource的名称(切换数据源时用到,对应下面的DataSourceKey类型)值为对应的数据源的引用,如下多数据源配置

<bean id="dataSource1" class="org.apache.tomcat.jdbc.pool.DataSource">
    <property name="poolProperties">
        <property name="dirverClassName" value="oracle.jdbc.driver.OracleDriver" />
        <property name="url" value="jdbc:oracle:thin:@10.165.111.11:6521:dataBaseName1" />
    </property>
    <property name="username" value="name1" />
    <property name="password" value="123456" />
</bean>

<bean id="dataSource2" class="org.apache.tomcat.jdbc.pool.DataSource">
    <property name="poolProperties">
        <property name="dirverClassName" value="oracle.jdbc.driver.OracleDriver" />
        <property name="url" value="jdbc:oracle:thin:@10.165.111.12:6521:dataBaseName2" />
    </property>
    <property name="username" value="name2" />
    <property name="password" value="123456" />
</bean>

<bean id="multiDataSource" class="com.uttp.MultiDataSource">
    <property name="defaultTargetDataSource" ref="dataSource1" />
    <property name="targetDataSources">
        <map>
            <entry key="readAndWrite" value-ref="dataSource1" />
            <entry key="read" vaule-ref=dataSource2 />
        </map>
    </property>
</bean>

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="multiDataSource" />
</bean>

其中 MultiDataSource继承自AbstractRoutingDataSource,其实现了抽象接口determineCurrentLookupKey()

public class MultiDataSource  extends AbstractRoutingDataSource {
    private static final ThreadLocal<String> dataSourceKey = new InheritableThreadLocak<String>();
    public static void setDataSourceKey(String dataSource) {
        dataSourceKey.set(dataSource);
    }
    @Override
    protected Object determineCurrentLookupKey() {
        return dataSourceKey.get();
    }
}

在上面配置中AbstractRoutingDataSource属性targetDataSource对应的是所有候选数据源,通过determinCurrentLookupKey方法获取当前需要访问数据库的数据源。现在的问题是怎么改变determinCurrentLookupKey这个方法获取数据源的值,通过Spring提供的AOP的功能可以在多数据源之间来回切换。首先需要定义多数据源名称的注解

@Target({ElementType.METHOD, ELEMENTTYPE.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface DataSource {
    public DataSourceKey value() default DataSourceKey.readAndWrite

    public enum DataSourceKey {
        readAndWrite,read;
    }
}

然后,需要对使用@DataSource注解的数据库操作进行拦截,然后修改数据源

@Aspect
@Service
class MultiDataSourceAspect {
    @PointCut("@within(DataSource)")
    public void pointCut() {
    }

    @PointCut("@annotation(DataSource)")
    public void pointCut1() {
    }

    @Around("(pointCut()||pointCut1())&&target(obj)")
    public Object doAround(ProceedingJoinPoint pjp, Object ojb) throws Throwable {
        Signature signature = pjp.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        DataSource dataSource = obj.getClass().getMethod(methodSignature.getName(), methodSignature.getParameterTypes()).getAnnotation(DataSource.class);
        if (dataSource == null) {
            dataSource = (DataSource) ojb.getClass().getAnnotation(DataSource.class);
        }
        if (dataSource == null) {
            dataSource = (dataSource) signature.getDeclaringType().getAnnotation(DataSource.class);
        }
        if(dataSource != null) {
            MultiDataSource.setDataSourceKey(dataSource.value().toString());
        }
        return pjp.proceed();
    }
}

当类或者方法中用到@DataSource(DataSourceKey.read)时,则会切换为只读数据库源

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值