Spring多数据源实现

来源:互联网 发布:编程兼职网站有哪些 编辑:程序博客网 时间:2024/05/01 00:50

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@Documentedpublic @interface DataSource {    public DataSourceKey value() default DataSourceKey.readAndWrite    public enum DataSourceKey {        readAndWrite,read;    }}

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

@Aspect@Serviceclass 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)时,则会切换为只读数据库源

0 0
原创粉丝点击