SPRING+MYBATIS多数据源切换(注解方式配置)
应公司要求,需要在项目中切换多个数据源,在百度其他大牛的总结后,决定自己总结一下,如有不完善的地方还请大家多多提出建议,废话不多说,进入主题
- 目录结构
2.模块介绍
如上图所示,由于项目采用MAVEN聚合工程设计,为达到配置文件公用的效果,集中将配置文件放在config工程中
我的项目目录是 config工程下的src/main/resources/ 我这里account模块需要引用config中spring的配置
3.配置文件打包
将配置文件放在公共配置的config目录中(如果是用idea的朋友) 请在config/pom文件中加入如下配置
(idea在maven打包的时候不会把xml文件打包进去,这里加入如下配置即可)
<build> <resources> <resource> <directory>${basedir}/src/main/java</directory> <includes> <include>**/*.xml</include> </includes> </resource> <resource> <directory>${basedir}src/main/resources</directory> <includes> <include>**.*</include> <include>**/**/*.xml</include> </includes> </resource> </resources> </build>
4.配置文件的依赖
上述3配置好后,接下来就是如何依赖config中的配置文件,在要依赖的项目模块中插件
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <executions> <execution> <id>unpack</id> <phase>package</phase> <goals> <goal>unpack</goal> </goals> <configuration> <artifactItems> <artifactItem> <groupId>com.wkp</groupId> <artifactId>wkp-config</artifactId> <!--idea 下使用此配置 注意前后端配置--> <outputDirectory>${project.build.directory}/wkp-account-server/WEB-INF/classes</outputDirectory> <!-- <outputDirectory>${project.build.directory}/classes</outputDirectory> --> <includes> **/conf/spring/spring-mybatis.xml </includes> <!--Eclispe下使用此配置--> </artifactItem> </artifactItems> </configuration> </execution> </executions> </plugin> </plugins>
在打包的时候回回从config中copy到要依赖的目录中目录按照自己的配置即可
5,配置多数据源
上述准备工作完成后,开始配置数据源 我的数据源是配置在spring-mybatis.xml中
<bean id="baseDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close" abstract="true"> <!-- 连接池最大值--> <property name="maxActive" value="${payCenter.db.maxActive}"/> <!-- 初始化大小 --> <property name="initialSize" value="${payCenter.db.initialSize}"/> <!-- 获取连接最大等待时间 --> <property name="maxWait" value="${payCenter.db.maxWait}"/> <!-- 连接池最小空闲 --> <property name="minIdle" value="${payCenter.db.minIdle}"/> <!-- 逐出连接的检测时间间隔 --> <property name="timeBetweenEvictionRunsMillis" value="${payCenter.db.timeBetweenEvictionRunsMillis}"/> <!-- 最小逐出时间 --> <property name="minEvictableIdleTimeMillis" value="${payCenter.db.minEvictableIdleTimeMillis}"/> <!-- 检测连接是否有效的SQL--> <property name="validationQuery" value="${payCenter.db.validationQuery}"/> <!-- 借出连接时是否做测试--> <property name="testWhileIdle" value="${payCenter.db.testWhileIdle}"/> <!-- 借出连接时是否做测试--> <property name="testOnBorrow" value="${payCenter.db.testOnBorrow}"/> <!--归还连接时是否做测试--> <property name="testOnReturn" value="${payCenter.db.testOnReturn}"/> <!-- 超过时间限制是否回收 --> <property name="removeAbandoned" value="${payCenter.db.removeAbandoned}"/> <!-- 超过时间限制多长--> <property name="removeAbandonedTimeout" value="${payCenter.db.removeAbandonedTimeout}"/> <!--借出连接时是否做测试--> <property name="poolPreparedStatements" value="true"/> <!--Statement缓存大小--> <property name="maxPoolPreparedStatementPerConnectionSize" value="20"/> </bean>
<!-- ========================================分隔线========================================= --> <!--多数据源配置 start--> <!--数据源 :开发库--> <bean id="local" parent="baseDataSource"> <property name="url" value="${deve.url}"/> <property name="username" value="${deve.username}"/> <property name="password" value="${deve.password}"/> </bean> <!--数据源 :测试库--> <bean id="server" parent="baseDataSource"> <property name="url" value="${test.url}"/> <property name="username" value="${test.username}"/> <property name="password" value="${test.password}"/> </bean> <bean id="dataSource" class="com.DynamicDataSource"> <property name="targetDataSources"> <map key-type="java.lang.String"> <entry key="local" value-ref="local"/> <entry key="server" value-ref="server"/> </map> </property> <!-- 默认使用server的数据源 --> <property name="defaultTargetDataSource" ref="local"></property> </bean> <!--多数据源配置 end--> <!-- ========================================数据源配置========================================= -->
dataSource这个bean中 配置多少数据源就要加多少个entity的节点
6.创建类
为了达到每个模块都能使用,我是将这些类放在common模块中,根据自己的配置来
DataSource.java
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 数据源 * * @author jiangnan * * @see com.stech.system.interceptor.DataSourceAspect */ @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface DataSource { String value() default ""; }
DynamicDataSource.java
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; /** * Created by Administrator on 2018/5/18. */ public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DynamicDataSourceHolder.getDataSourceType(); } }
DynamicDataSourceHolder.java
public class DynamicDataSourceHolder { private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(); /** * @param dataSourceType 数据库类型 * @return void * @throws * @Description: 设置数据源类型 */ public static void setDataSourceType(String dataSourceType) { contextHolder.set(dataSourceType); } /** * @param * @return String * @throws * @Description: 获取数据源类型 */ public static String getDataSourceType() { return contextHolder.get(); } /** * @param * @return void * @throws * @Description: 清除数据源类型 */ public static void clearDataSourceType() { contextHolder.remove(); } }
DynamicDataSourceInterceptor.java
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.reflect.MethodSignature; import java.lang.reflect.Method; /** * Created by Administrator on 2018/5/18. */ public class DynamicDataSourceInterceptor { /** * 拦截目标方法,获取由@DataSource指定的数据源标识,设置到线程存储中以便切换数据源 * * @param point * @throws Exception */ public void intercept(JoinPoint point) throws Exception { Class<?> target = point.getTarget().getClass(); MethodSignature signature = (MethodSignature) point.getSignature(); resolveDataSource(target, signature.getMethod()); } /** * 提取目标对象方法注解和类型注解中的数据源标识 * */ private void resolveDataSource(Class<?> clazz, Method method) { try { Class<?>[] types = method.getParameterTypes(); if (clazz.isAnnotationPresent(DataSource.class)) { DataSource source = clazz.getAnnotation(DataSource.class); DynamicDataSourceHolder.setDataSourceType(source.value()); } Method m = clazz.getMethod(method.getName(), types); if (m != null && m.isAnnotationPresent(DataSource.class)) { DataSource source = m.getAnnotation(DataSource.class); DynamicDataSourceHolder.setDataSourceType(source.value()); } } catch (Exception e) { e.printStackTrace(); } }
7.引用
@DataSource(DataSourceConstans.server) public int insert(List value){ return mapper.insert(value); }
8。注意
数据源切换的时候会影响事物,本人是将service 和dao直接加了一层repository 在service中控制事物在repository中切换数据源,具体情况还要看具体的应用,很多人在control层进行切换数据源
项目中的配置只是一些主要的数据源方面的配置,详细配置还要根据各位的具体场景来应用,如果有不妥之处还请各位大神多多留言,共同进步,共同成长
-------蒙着脸的驴