spring 使用AbstractRoutingDataSource自定义动态数据源时的事务处理问题
2014-09-23 10:55
513 查看
最近在网上看到了一篇博客,继承spring的AbstractRoutingDataSource定义自己的动态数据源,可以根据需要动态的切换不同数据库的数据源,感觉使用起来非常方便。
通过容器RouteHolder存储当前线程使用的数据源的key
使用spring 的aop编程在业务逻辑方法运行前将当前方法使用数据源的key从业务逻辑方法上自定义注解@DataSource中解析数据源key并添加到RouteHolder中
业务逻辑方法
spring的配置文件
事务管理配置一定要配置在往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 使用动态数据源。 不需要再业务逻辑方法层中定义不同的数据源对象,人为的使用不同的数据源操作数据。
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; /** * 自定义的动态路由数据源 继承自 spring jdbc的AbstractRoutingDataSource * * @author * */ public class DbRouteDataSource extends AbstractRoutingDataSource { /** * 获取与数据源相关的key * 此key是Map<String,DataSource> resolvedDataSources 中与数据源绑定的key值 * 在通过determineTargetDataSource获取目标数据源时使用 */ @Override protected 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 { @Inject private 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 使用动态数据源。 不需要再业务逻辑方法层中定义不同的数据源对象,人为的使用不同的数据源操作数据。
相关文章推荐
- 基于spring多数据源动态调用及其事务处理
- 详解基于spring多数据源动态调用及其事务处理
- 基于spring多数据源动态调用及其事务处理
- 使用proxool连接sybase时处理事务的问题
- Spring 多数据源事务配置问题
- spring动态创建,加载,使用多数据源
- Spring 多数据源事务配置问题
- 使用spring的事务控制,这种情况如何处理呢
- Spring 多数据源事务配置问题
- 多数据源的动态配置与加载使用兼框架交互的问题调试
- spring动态创建切换多数据源问题分析与解决办法
- ASP.NET MVC3 中整合 NHibernate3.3、Spring.NET2.0 使用AOP执行事务处理
- Transaction事务注解和DynamicDataSource动态数据源切换问题解决
- ADF:如何使用VO处理查询条件的值属于某一动态值列表的问题
- 关于Spring3 + Mybatis3整合时,多数据源动态切换的问题
- Spring 多数据源事务配置问题
- ORA-14450: 试图访问已经在使用的事务处理临时表,根据网上文章提供的方法,问题是解决了
- Spring 多数据源事务配置问题
- Spring动态创建,加载,使用多数据源
- 关于Spring3 + Mybatis3整合时,多数据源动态切换的问题(二)