SpringMVC+Mybatis 多数据源配置

浪费了一上午的时间,参考了各种大神的帖子,并不好用。有的是能用,但不支持事务,有的压根因为配置习惯的不同或者spring版本不一样,配置也不能用,这里粘贴下我自己的配置,希望能帮你找到一些思路,如果能直接集成成功,那是最好啦
废话不多说,直接上代码。
1、项目为Spring-SpringMVC-Mybatis maven
2、SpringMVC 版本 4.3
3、开发工具 IDEA
4、数据库连接池用的阿里 druid
主要修改以下配置:
- jdbc.properties
- springMvcContext-base.xml
- 增加相关代码块

1、修改jdbc.properties


driver=com.microsoft.sqlserver.jdbc.SQLServerDriver
url=jdbc:sqlserver://192.168.0.188:1433;databaseName=xxxxxxx
username=sa
password=sa
validationQuery:select 1

driver_two=oracle.jdbc.driver.OracleDriver
url_two=jdbc:oracle:thin:@192.168.0.188:1521:ORCL
username_two=sa
password_two=sa
validationQuery_two:select 1 from dual

filters:stat
maxActive:20
initialSize:1
maxWait:60000
minIdle:10
maxIdle:15

timeBetweenEvictionRunsMillis:60000
minEvictableIdleTimeMillis:300000


testWhileIdle:true
testOnBorrow:false
testOnReturn:false

maxOpenPreparedStatements:20
removeAbandoned:true
removeAbandonedTimeout:1800
logAbandoned:true

2、修改springMvcContext-base.xml
(只有核心配置,其他的不贴了)

<!-- 配置数据源_2:阿里 druid数据库连接池  -->
    <bean id="dataSource_sqlserver" class="com.alibaba.druid.pool.DruidDataSource"
        destroy-method="close">
        <!-- 数据库基本信息配置 -->
        <property name="url" value="${url}" />
        <property name="username" value="${username}" />
        <property name="password" value="${password}" />
        <property name="driverClassName" value="${driver}" />
        <property name="filters" value="${filters}" />
        <!-- 最大并发连接数 -->
        <property name="maxActive" value="${maxActive}" />
        <!-- 初始化连接数量 -->
        <property name="initialSize" value="${initialSize}" />
        <!-- 配置获取连接等待超时的时间 -->
        <property name="maxWait" value="${maxWait}" />
        <!-- 最小空闲连接数 -->
        <property name="minIdle" value="${minIdle}" />
        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
        <property name="timeBetweenEvictionRunsMillis" value="${timeBetweenEvictionRunsMillis}" />
        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
        <property name="minEvictableIdleTimeMillis" value="${minEvictableIdleTimeMillis}" />
        <property name="validationQuery" value="${validationQuery}" />
        <property name="testWhileIdle" value="${testWhileIdle}" />
        <property name="testOnBorrow" value="${testOnBorrow}" />
        <property name="testOnReturn" value="${testOnReturn}" />
        <property name="maxOpenPreparedStatements" value="${maxOpenPreparedStatements}" />
        <!-- 打开removeAbandoned功能 -->
        <property name="removeAbandoned" value="${removeAbandoned}" />
        <!-- 1800秒,也就是30分钟 -->
        <property name="removeAbandonedTimeout" value="${removeAbandonedTimeout}" />
        <!-- 关闭abanded连接时输出错误日志 -->
        <property name="logAbandoned" value="${logAbandoned}" />
    </bean>

    <bean id="dataSource_oracle" class="com.alibaba.druid.pool.DruidDataSource"
          destroy-method="close">
        <!-- 数据库基本信息配置 -->
        <property name="url" value="${url_two}" />
        <property name="username" value="${username_two}" />
        <property name="password" value="${password_two}" />
        <property name="driverClassName" value="${driver_two}" />
        <property name="filters" value="${filters}" />
        <!-- 最大并发连接数 -->
        <property name="maxActive" value="${maxActive}" />
        <!-- 初始化连接数量 -->
        <property name="initialSize" value="${initialSize}" />
        <!-- 配置获取连接等待超时的时间 -->
        <property name="maxWait" value="${maxWait}" />
        <!-- 最小空闲连接数 -->
        <property name="minIdle" value="${minIdle}" />
        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
        <property name="timeBetweenEvictionRunsMillis" value="${timeBetweenEvictionRunsMillis}" />
        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
        <property name="minEvictableIdleTimeMillis" value="${minEvictableIdleTimeMillis}" />
        <property name="validationQuery" value="${validationQuery_two}" />
        <property name="testWhileIdle" value="${testWhileIdle}" />
        <property name="testOnBorrow" value="${testOnBorrow}" />
        <property name="testOnReturn" value="${testOnReturn}" />
        <property name="maxOpenPreparedStatements" value="${maxOpenPreparedStatements}" />
        <!-- 打开removeAbandoned功能 -->
        <property name="removeAbandoned" value="${removeAbandoned}" />
        <!-- 1800秒,也就是30分钟 -->
        <property name="removeAbandonedTimeout" value="${removeAbandonedTimeout}" />
        <!-- 关闭abanded连接时输出错误日志 -->
        <property name="logAbandoned" value="${logAbandoned}" />
    </bean>

关键在此
----------


    <!--双数据源-->
    <bean id="dataSource" class="com.googosoft.util.double_datasource.DynamicDataSource">
        <property name="targetDataSources">
            <map key-type="java.lang.String">
                <entry value-ref="dataSource_sqlserver" key="dataSource_sqlserver"></entry><!--数据源的名字-->
                <entry value-ref="dataSource_oracle" key="dataSource_oracle"></entry>
            </map><!--数据源的名字-->
        </property>
    </bean>
    <!-- 配置数据库注解aop -->
    <bean id="dataSourceAspect" class="com.alibaba.util.double_datasource.DataSourceAspect"/>

    <!-- 使用aop技术实现事物处理 -->
    <aop:config>
        <!-- id:事务切入点名称 expression:事务切入点正则表达式 -->
        <aop:pointcut id="serviceMethods" expression="execution(* com.alibaba.serviceImpl..*.*(..))" />
<!--        expression="execution(* com.alibaba.serviceImpl.*.*(..))" /> -->
        <!-- pointcut-ref:事务切入点名称 advice-ref:事务通知名称 -->
        <!--数据源选择切面,保证在事务开始之前执行-->
        <aop:advisor pointcut-ref="serviceMethods" advice-ref="dataSourceAspect" order="1" /><!--数据源切面执行顺序-->
        <aop:advisor pointcut-ref="serviceMethods" advice-ref="txAdvice" order="2" /><!--数据源的名字-->
    </aop:config>


----------


    <!-- 配置Session工厂 -->
    <!-- spring和MyBatis整合-->  
    <bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">  
        <property name="dataSource" ref="dataSource" />
        <!-- 指明mybatis主配置文件路径 -->
        <property name="configLocation" value="classpath:mybatis.cfg.xml"></property>
        <!-- 指明mybatis的映射位置  自动扫描需要定义类别名的包,将包内的JAVA类的类名作为类别名-->
        <property name="mapperLocations">
            <list>
                <value>classpath:orm/**/*.xml</value>
            </list>
        </property>
    </bean>
<!-- DAO接口所在包名,Spring会自动查找其下的类 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.alibaba.mapper" />
        <property name="sqlSessionFactoryBeanName" value="sessionFactory"></property>
    </bean>


    <!-- 配置SqlSession模版类-->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate" scope="prototype">
        <constructor-arg  ref="sessionFactory"></constructor-arg>       
    </bean>

    <!-- 事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- 配置事务通知 id:事务通知名称,transaction-manager:指定事务管理器名称 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!-- 以delete、insert、update、sava、do、go开头的所有方法采用只读型事务控制类型 -->
            <tx:method name="delete*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception" />
            <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="save*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception" />
            <tx:method name="do*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception" timeout="10" />
            <tx:method name="go*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception" timeout="10" />
            <!-- 以load、find、search、select、get开头的所有方法采用只读型事务控制类型 -->
            <tx:method name="load*" propagation="SUPPORTS" read-only="true"/>
            <tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
            <tx:method name="search*" propagation="SUPPORTS" read-only="true"/>
            <tx:method name="select*" propagation="SUPPORTS" read-only="true"/>
            <tx:method name="get*" propagation="SUPPORTS" read-only="true"/>
            <tx:method name="*" propagation="SUPPORTS" read-only="true"/>
        </tx:attributes>
    </tx:advice>


3、增加相关代码块
注:利用注解实现动态切换相关数据源
DynamicDataSource.class

/**
 * 类名:DynamicDataSource.class
 * 描述:动态数据源类
 * --------------------------------------
 * 修改内容:
 * 备注:
 * Modify by:
 */
public class DynamicDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        String dataSource =DynamicDataSourceHolder.getDataSourceType();
        return dataSource;
    }

}

DynamicDataSourceHolder.class

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
 * 类名:DynamicDataSourceHolder.class
 * 描述:获得和设置上下文环境的类,主要负责改变上下文数据源的名称
 * --------------------------------------
 * 修改内容:
 * 备注:
 * Modify by:
 */
public class DynamicDataSourceHolder {
    private static final ThreadLocal<String> contextHolder= new ThreadLocal<String>();

    public static void setDataSourceType(String dataSourceType) {
        contextHolder.set(dataSourceType);
    }

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

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



DataSourceAspect.class

import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;
/**
 * 类名:DataSourceAspect.class
 * 描述:数据源切面支持,在开启事务之前选择默认的数据源
 * --------------------------------------
 * 修改内容:
 * 备注:
 * Modify by:
 */
public class DataSourceAspect implements MethodBeforeAdvice,AfterReturningAdvice
{
    public static final String dataSource_sqlserver = "dataSource_sqlserver";
    public static final String dataSource_oracle = "dataSource_oracle";

    @Override
    public void afterReturning(Object returnValue, Method method,
                               Object[] args, Object target) throws Throwable {

        DynamicDataSourceHolder.clearDataSourceType();
    }

    @Override
    public void before(Method method, Object[] args, Object target)
            throws Throwable {

        //首先取类上的数据源
        if(method.getDeclaringClass().isAnnotationPresent(DataSource.class) && !method.isAnnotationPresent(DataSource.class)) {

            DataSource datasource = method.getDeclaringClass().getAnnotation(DataSource.class);
            DynamicDataSourceHolder.setDataSourceType(datasource.name());

            //方法上的数据源 优先级高于类上的
        } else if (method.isAnnotationPresent(DataSource.class)) {

            DataSource datasource = method.getAnnotation(DataSource.class);
            DynamicDataSourceHolder.setDataSourceType(datasource.name());
        }
        else
        {
            DynamicDataSourceHolder.setDataSourceType(dataSource_sqlserver);
        }
    }
}

DataSource.class


import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
 * 类名:DataSource.class
 * 描述:数据源切换注解
 * --------------------------------------
 * 修改内容:
 * 备注:
 * Modify by:
 */
@Documented
@Retention(RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})

public @interface DataSource {
    String name();
//    String name() default DataSource.dataSource_sqlserver;
}

调用方法
在如果你的事务控制在service层,建议在controller下调用 添加如下代码块(支持在类和方法级调用)

@DataSource(name = DataSourceAspect.dataSource_oracle)

关于一些疑问:
动态切换数据源以后,需要再切换回来吗?答案:不需要,因为之前已经设置默认的数据源,只有添加注解DataSource的地方,才能改变数据源,如果其他类或者方法没有添加此注解DataSource,将用默认数据源执行数据库操作。
所以,建议在项目初始就将访问不同数据源的controller 或者业务进行分包管理,防止代码混乱

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值