spring 使用AbstractRoutingDataSource自定义动态数据源时的事务处理问题

来源:互联网 发布:企业管理论文选题 知乎 编辑:程序博客网 时间:2024/05/22 03:14


最近在网上看到了一篇博客,继承spring的AbstractRoutingDataSource定义自己的动态数据源,可以根据需要动态的切换不同数据库的数据源,感觉使用起来非常方便。

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;/** * 自定义的动态路由数据源 继承自 spring jdbc的AbstractRoutingDataSource *  * @author  * */public class DbRouteDataSource extends AbstractRoutingDataSource {/** * 获取与数据源相关的key * 此key是Map<String,DataSource> resolvedDataSources 中与数据源绑定的key值 * 在通过determineTargetDataSource获取目标数据源时使用 */@Overrideprotected Object determineCurrentLookupKey() {return RouteHolder.getRouteKey();}}

通过容器RouteHolder存储当前线程使用的数据源的key

/** * 保存当前线程数据源的key * @author  * @version 1.0 * */public class RouteHolder {private static ThreadLocal<String> routeKey = new ThreadLocal<String>();/** * 获取当前线程的数据源路由的key * @return */public static String getRouteKey(){String key = routeKey.get();return key;}/** * 绑定当前线程数据源路由的key * 使用完成后必须调用removeRouteKey()方法删除 * @param key */public static void  setRouteKey(String key){routeKey.set(key);}/** * 删除与当前线程绑定的数据源路由的key */public static void removeRouteKey(){routeKey.remove();}}


使用spring 的aop编程在业务逻辑方法运行前将当前方法使用数据源的key从业务逻辑方法上自定义注解@DataSource中解析数据源key并添加到RouteHolder中

/** * 执行dao方法之前的切面 * 获取datasource对象之前往RouteHolder中指定当前线程数据源路由的key * @author Administrator * */public class DataSourceAspect{/** * 在dao层方法之前获取datasource对象之前在切面中指定当前线程数据源路由的key */public void beforeDaoMethod(JoinPoint point){//dao方法上配置的注解DataSource datasource = ((MethodSignature)point.getSignature()).getMethod().getAnnotation(DataSource.class);RouteHolder.setRouteKey(datasource.value());}}

业务逻辑方法

@Named("userService")public class UserService {@Injectprivate UserDao userDao;@DataSource("master")@Transactional(propagation=Propagation.REQUIRED)public void updatePasswd(int userid,String passwd){User user = new User();user.setUserid(userid);user.setPassword(passwd);userDao.updatePassword(user);}@DataSource("slave")@Transactional(propagation=Propagation.REQUIRED)public User getUser(int userid){User user = userDao.getUserById(userid);System.out.println("username------:"+user.getUsername());return user;}}

spring的配置文件

<bean id="dataSource" class="com.westone.datasource.DbRouteDataSource"><property name="targetDataSources"><map>   <!-- write -->   <entry key="master" value-ref="master"></entry>   <!-- read -->   <entry key="slave" value-ref="slave"></entry></map></property></bean><bean id="master" class="org.apache.commons.dbcp.BasicDataSource">          <property name="driverClassName" value="${jdbc.driverclass}" />          <property name="url" value="${jdbc.masterurl}" />          <property name="username" value="${jdbc.username}" />          <property name="password" value="${jdbc.password}" />          <property name="maxActive" value="${jdbc.maxActive}"></property>          <property name="maxIdle" value="${jdbc.maxIdle}"></property>          <property name="maxWait" value="${jdbc.maxWait}"></property>      </bean>          <bean id="slave" class="org.apache.commons.dbcp.BasicDataSource">          <property name="driverClassName" value="${jdbc.driverclass}" />          <property name="url" value="${jdbc.slaveurl}" />          <property name="username" value="${jdbc.username}" />          <property name="password" value="${jdbc.password}" />          <property name="maxActive" value="${jdbc.maxActive}"></property>          <property name="maxIdle" value="${jdbc.maxIdle}"></property>          <property name="maxWait" value="${jdbc.maxWait}"></property>      </bean>                      <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate" >          <constructor-arg index="0" ref="sqlSessionFactory" />    </bean>    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">          <property name="configLocation" value="classpath:config/mybatis/mybatis.cfg.xml"></property>          <property name="dataSource" ref="dataSource" />      </bean>         <!--  配置mapper的映射扫描器 根据包中定义的接口自动生成dao的实现类-->    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">      <property name="basePackage" value="com.westone.dao"></property>    </bean>         <!-- 为业务逻辑层的方法解析@DataSource注解  为当前线程的routeholder注入数据源key -->   <bean id="aspectBean" class="com.westone.datasource.aspect.DataSourceAspect"></bean>        <aop:config>    <aop:aspect id="dataSourceAspect" ref="aspectBean">       <aop:pointcut id="dataSourcePoint" expression="execution(public * com.westone.service.*.*(..))" />       <aop:before method="beforeDaoMethod" pointcut-ref="dataSourcePoint"/>    </aop:aspect>    </aop:config>          <!-- 事务管理器配置 -->    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">          <property name="dataSource" ref="dataSource" />      </bean> <!-- 开启事务注解驱动    在业务逻辑层上使用@Transactional 注解 为业务逻辑层管理事务-->      <tx:annotation-driven  transaction-manager="transactionManager"/>

事务管理配置一定要配置在往RouteHolder中注入数据源key之前  否则会报 Could not open JDBC Connection for transaction; nested exception is java.lang.IllegalStateException: Cannot determine target DataSource for lookup key [null]   找不到数据源错误


最后测试业务逻辑层的方法发现   可以根据方法上的@DataSource("master")  注解配置不同的数据源key  使用动态数据源。  不需要再业务逻辑方法层中定义不同的数据源对象,人为的使用不同的数据源操作数据。




0 0
原创粉丝点击