SpringMVC+Mybatis 多数据源配置

来源:互联网 发布:水泥凝结时间测定数据 编辑:程序博客网 时间:2024/06/05 00:09

浪费了一上午的时间,参考了各种大神的帖子,并不好用。有的是能用,但不支持事务,有的压根因为配置习惯的不同或者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.SQLServerDriverurl=jdbc:sqlserver://192.168.0.188:1433;databaseName=xxxxxxxusername=sapassword=savalidationQuery:select 1driver_two=oracle.jdbc.driver.OracleDriverurl_two=jdbc:oracle:thin:@192.168.0.188:1521:ORCLusername_two=sapassword_two=savalidationQuery_two:select 1 from dualfilters:statmaxActive:20initialSize:1maxWait:60000minIdle:10maxIdle:15timeBetweenEvictionRunsMillis:60000minEvictableIdleTimeMillis:300000testWhileIdle:truetestOnBorrow:falsetestOnReturn:falsemaxOpenPreparedStatements:20removeAbandoned:trueremoveAbandonedTimeout:1800logAbandoned: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 或者业务进行分包管理,防止代码混乱

原创粉丝点击