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

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

2014-09-23 10:55 513 查看
最近在网上看到了一篇博客,继承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获取目标数据源时使用
*/
@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 使用动态数据源。 不需要再业务逻辑方法层中定义不同的数据源对象,人为的使用不同的数据源操作数据。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: