spring+mybatis数据源切换【service层以及controller层】

spring+mybatis数据源切换【service层以及controller层】

最近项目需要切换数据源,查询了相关资料后做了一个简单的数据源的切换。

一、基本的简单配置

首先是我认为比较好的切换是在service层进行切换,基本思想是利用spring的AbstractRoutingDataSource类进行datasource的选择,就像map的key-value,AbstractRoutingDataSource是有个determineCurrentLookupKey选择datasource的bean进行切换。

下面是java类的配置

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class DynamicDataSource extends AbstractRoutingDataSource{
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataSource();
    }
}

/**数据源的管理类**/

public class DataSourceContextHolder {
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();

    public static final String DEFAULT_DATA_SOURCE="defaultDataSource"; //默认数据源

    public static final String READ_DATA_SOURCE="readDataSource";       //第二个数据源,因为这个数据源是只提供读取,所以叫read...

    /**
     * 设置数据源类型
     * @param dataSource 数据源名称
     */
    public static void setDataSource(String dataSource) {
        contextHolder.set(dataSource);
    }

    /**
     * 获取数据源名称
     */
    public static String getDataSource() {
        return contextHolder.get();
    }

    /**
     * 清除数据源名称
     */
    public static void clearDataSource() {
        contextHolder.remove();
    }
}

接下来要在xml文件中配置动态的数据源

部分配置文件如下:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dynamicDataSource"/>
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath*:/com/test/core/domain/mapper/**/*.xml"/>
    </bean>

    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>classpath:/properties/app-${env}.properties</value>
            </list>
        </property>
    </bean>
    
    <!-- 数据源1-->
    <bean id="defaultDataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
        <property name="driverClassName" value="${datasource.classname}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
        <property name="connectionTimeout" value="${connection.timeout}"/>
        <property name="maximumPoolSize" value="${max.pool.size}"/>
        <property name="leakDetectionThreshold" value="${leak.detection.threshold}"/>
    </bean>
    <!-- 数据源2-->
    <bean id="readDataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
        <property name="driverClassName" value="${read.datasource.classname}"/>
        <property name="jdbcUrl" value="${read.jdbc.url}"/>
        <property name="username" value="${read.username}"/>
        <property name="password" value="${read.password}"/>
        <property name="connectionTimeout" value="${read.connection.timeout}"/>
        <property name="maximumPoolSize" value="${read.max.pool.size}"/>
        <property name="leakDetectionThreshold" value="${read.leak.detection.threshold}"/>
    </bean>

    <!-- 动态数据源-->
    <bean id="dynamicDataSource" class="com.test.datasource.DynamicDataSource">
        <property name="targetDataSources">
            <map key-type="java.lang.String">
                <entry value-ref="defaultDataSource" key="defaultDataSource"/>
                <entry value-ref="readDataSource" key="readDataSource"/>
            </map>
        </property>
        <!-- 默认数据源为defaultDataSource-->
        <property name="defaultTargetDataSource" ref="defaultDataSource"/>
    </bean>

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dynamicDataSource"/>
    </bean>

    <tx:annotation-driven mode="proxy" transaction-manager="transactionManager"/>

    <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.test.core.domain.mapper"/>
        <property name="annotationClass" value="org.springframework.stereotype.Repository"/>
    </bean>

做好了这些工作就可以在dao层进行数据源的切换了,

 如:DataSourceContextHolder.setDataSource(DataSourceContextHolder.READ_DATA_SOURCE);

二、利用spring的aop实现在service切换数据源

然而我们需要在service层或者controller层进行切换,而在进入service时已经开启了一个事务,这时候直接切换数据源是无效的,所以要在service前就切换数据源。

利用spring 的aop来实现我们的想法吧

切换数据源的java类:

/**切面**/

import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;
@Slf4j
public class DataSourceExchange implements MethodBeforeAdvice,AfterReturningAdvice{
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        DataSourceContextHolder.clearDataSource();
        log.info("del datasource");
    }

    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
       
        if (method.isAnnotationPresent(DataSource.class))
        {
            DataSource datasource = method.getAnnotation(DataSource.class);
            DataSourceContextHolder.setDataSource(datasource.name());
        }
        else
        {
            DataSourceContextHolder.setDataSource(DataSourceContextHolder.DEFAULT_DATA_SOURCE);
        }

        log.info("now datasource:"+DataSourceContextHolder.getDataSource());
    }
}

/**为了更好的体验,直接用注解来定义数据源**/

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
    String name() default "defaultDataSource";
}

xml的相关配置

<!--1.配置Spring框架自身提供的切面类-->
    <tx:advice id="userTxAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="delete*" propagation="REQUIRED" read-only="false"
                       rollback-for="java.lang.Exception" no-rollback-for="java.lang.RuntimeException" />
            <tx:method name="insert*" propagation="REQUIRED" read-only="false"
                       rollback-for="java.lang.Exception" />
            <tx:method name="update*" propagation="REQUIRED" read-only="false"
                       rollback-for="java.lang.Exception" />
            <tx:method name="find*" propagation="SUPPORTS" />
            <tx:method name="get*" propagation="SUPPORTS" />
            <tx:method name="select*" propagation="SUPPORTS" />
        </tx:attributes>
    </tx:advice>
    <!-- 2.配置自己定义的切面类 -->
    <bean id="dataSourceExchange" class="com.chejianer.admin.datasource.DataSourceExchange"/>
    <!-- 3.根据切面动态切换数据源-->
    <aop:config>
        <aop:pointcut id="service" expression="execution(* com.test.service.*.*(..))" />
        <aop:advisor pointcut-ref="service" advice-ref="userTxAdvice" order="2" />
        <aop:advisor pointcut-ref="service" advice-ref="dataSourceExchange" order="1" />
    </aop:config>

配置完成后就可以进行测试了,在serviceImpl的的方法上面加上@DataSource(name=你要切换的数据源)就ok了,这里就不贴代码了。

三、直接在controller进行切换,继续探索中

因为项目需要切数据源的地方特别多,在service层切换很费时间,所以准备在controller层进行切换,而尝试了下spring的切面在这里直接切不了,具体原因还没找到,所以暂时只能采用笨的方法,在controller的方法开头加上:

 DataSourceContextHolder.setDataSource(DataSourceContextHolder.READ_DATA_SOURCE);

方法结尾加上:

 DataSourceContextHolder.clearDataSource();

                                                                                                                                                                                                                                                                      2017-03-25   微笑

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/100317.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • PHP抖音SDK搭建问题整理

    PHP抖音SDK搭建问题整理

    2022年2月15日
    45
  • Spring 为啥默认把 bean 设计成单例的?

    熟悉Spring开发的朋友都知道Spring提供了5种scope分别是singleton、prototype、request、session、global session。 如下图…

    2021年6月22日
    88
  • 学术资源不定期分享-【费曼物理学讲义英文原版】[通俗易懂]

    学术资源不定期分享-【费曼物理学讲义英文原版】[通俗易懂]相关资料简介理查德·费曼(全名理查德·菲利普斯·费曼),(1918年5月11日生于美国纽约)他是美国理论物理学家,被广泛认为是二战后他的研究领域中最杰出、最具影响力的人物之一。费曼因他在量子电动力学方面的工作而闻名:他描述了光如何与物质相互作用以及带电粒子如何相互作用。他还设计了粒子如何相互作用的图表(现在称为费曼图)和液氦超流体行为的量子力学解释(接近绝对零度时如何在没有摩擦的情况下流动)。第二次世界大战期间,费曼被聘为普林斯顿大学美国原子弹项目的一名工作人员(1941-42年),后来又在新墨西哥.

    2022年6月6日
    56
  • iphone android换机助手下载,换机助手IOS版下载 换机助手(微信文件手机搬家) for iphone v1.0.13 苹果手机版 下载-脚本之家…

    iphone android换机助手下载,换机助手IOS版下载 换机助手(微信文件手机搬家) for iphone v1.0.13 苹果手机版 下载-脚本之家…换机助手IOS版是一款适用于苹果手机端的微信文件手机搬家软件,能够有效的对微信文件进行管理,可以在两台手机之间建立连接文件传输,感兴趣的朋友快来下载使用吧。软件功能能够在平台上通过扫描功能,快速的将两台手机进行连接;在平台上可以进行文件的传输,快速将文件传输到另外一台手机上;系统会将平台上的微信文件快速的进行识别,显示在平台上;平台上的文件信息都会根据类型的不同进行有效的分类;线上就能进行文件等信…

    2022年5月16日
    30
  • eclipse检出svn代码_系统出现乱码怎么办

    eclipse检出svn代码_系统出现乱码怎么办eclipse默认编码格式为GBK 将其更改为utf-8即可

    2022年10月14日
    0
  • 二元logistic回归模型——spss步骤

    二元logistic回归模型——spss步骤二元:因变量为二分类变量,且两个分类整合在一起的概率为1.(有效/无效;是/否)分析——回归——二元logistic——结果作为因变量——自变量作为协变量分类——设置分类变量(非连续变量)——变化量、第一个保存——概率、组成员选项:霍斯默-莱梅肖拟合优度、Exp(B)置信区间——在每一个步骤结果分析:(1)看霍斯默检验的显著性:sig/p>0.05表示拟合良好。(2)方程中的变量:B——系数sig——p值——显著性Exp(B)——OR值——优势比(高出一个单位,发生的概率高出多少

    2022年10月25日
    0

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号