您的位置:首页 > 编程语言 > Java开发

SpringMVC+Mybatis 多数据源配置

2017-12-01 15:10 393 查看
浪费了一上午的时间,参考了各种大神的帖子,并不好用。有的是能用,但不支持事务,有的压根因为配置习惯的不同或者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 或者业务进行分包管理,防止代码混乱
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: