spring+mybatis数据源切换【service层以及controller层】
来源:互联网 发布:香港中老年人奶粉知乎 编辑:程序博客网 时间:2024/06/04 21:49
最近项目需要切换数据源,查询了相关资料后做了一个简单的数据源的切换。
一、基本的简单配置
首先是我认为比较好的切换是在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;@Slf4jpublic 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)@Documentedpublic @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
- spring+mybatis数据源切换【service层以及controller层】
- JavaMelody监控Spring Service层和Spring Controller层
- DAO层,Service层,Controller层、…
- spring三层示例controller层,service层示例,dao层示例
- DAO层,Service层,Controller层、View层
- DAO层,Service层,Controller层、View层
- DAO层,Service层,Controller层、View层
- DAO层,Service层,Controller层、View层
- DAO层,Service层,Controller层、View层
- DAO层,Service层,Controller层、View层
- DAO层,Service层,Controller层,View层
- DAO层,Service层,Controller层、View层
- DAO层、Service层、Controller层、View层
- DAO层,Service层,Controller层、View层
- DAO层,Service层,Controller层、View层介绍
- DAO层,Service层,Controller层,View层
- DAO层,Service层,Controller层、View层
- Dao层、service层、Controller层、view层
- 《Go in Action 2015.11.pdf》之Go’s type system
- 泛型擦除的局限
- super()在程序中的实现与Object类中的toString()方法的实现
- 汉诺塔问题
- 字符串对比
- spring+mybatis数据源切换【service层以及controller层】
- c++ 栈机制
- JAVA读取并将图片转换成String
- 集合(2)—Collection之List的使用方法
- 关于重载和重写
- ES6---let与const
- 用Unity做游戏,你需要深入了解下IL2CPP
- OpenCV--C4996'fopen': This function or variable may be unsafe.
- 自己编写一个native方法