博客引用处(以下内容在原有博客基础上进行补充或更改,谢谢这些大牛的博客指导):
Datasource动态切换
spring 使用AbstractRoutingDataSource自定义动态数据源时的事务处理, 需要继承spring的AbstractRoutingDataSource定义自己的动态数据源,可以根据需要动态的切换不同数据库的数据源,使用起来非常方便。
public class ChooseDataSource extends AbstractRoutingDataSource {
/**
* 获取与数据源相关的key
* 此key是Map resolvedDataSources 中与数据源绑定的key值
* 在通过determineTargetDataSource获取目标数据源时使用
*/
@Override
protected Object determineCurrentLookupKey() {
return RouteHolder.getRouteKey();
}
}
通过容器RouteHolder存储当前线程使用的数据源的key
/**
* 保存当前线程数据源的key
*/
public class RouteHolder {
private static ThreadLocal routeKey = new ThreadLocal();
/**
* 获取当前线程的数据源路由的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
*
*/
public class DataSourceAspect {
/**
* 在dao层方法之前获取datasource对象之前在切面中指定当前线程数据源路由的key
*/
public void before(JoinPoint point)
{
Object target = point.getTarget();
String method = point.getSignature().getName();
Class>[] classz = target.getClass().getInterfaces();
Class>[] parameterTypes = ((MethodSignature) point.getSignature())
.getMethod().getParameterTypes();
try {
if(classz != null && classz.length > 0) {
Method m = classz[0].getMethod(method, parameterTypes);
if (m != null && m.isAnnotationPresent(DataSource.class)) {
DataSource data = m.getAnnotation(DataSource.class);
RouteHolder.setRouteKey(data.value());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
解释:
DataSourceAspect 这个切面类,应该针对的被代理类应该是service的实现类(serviceImpl),因为dao层用的mybatis只有一个dao层的接口,所以放在service上做处理比较好。
业务逻辑方法
@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;
}
}
注解类
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* RUNTIME
* 编译器将把注释记录在类文件中,在运行时 VM 将保留注释,因此可以反射性地读取。
* @author jiangxm
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DataSource {
String value();
}
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 java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;
public class MethodDemo {
public static void main(String[] args) {
Method[] methods = SampleClass.class.getMethods();
Annotation annotation = methods[0].getAnnotation(CustomAnnotation.class);
if(annotation instanceof CustomAnnotation){
CustomAnnotation customAnnotation = (CustomAnnotation) annotation;
System.out.println("name: " + customAnnotation.name());
System.out.println("value: " + customAnnotation.value());
}
}
}
@CustomAnnotation(name="SampleClass", value = "Sample Class Annotation")
class SampleClass {
private String sampleField;
@CustomAnnotation(name="getSampleMethod", value = "Sample Method Annotation")
public String getSampleField() {
return sampleField;
}
public void setSampleField(String sampleField) {
this.sampleField = sampleField;
}
}
@Retention(RetentionPolicy.RUNTIME)
@interface CustomAnnotation {
public String name();
public String value();
}
编译并运行上面的程序,这将产生以下结果
-name: getSampleMethod
value: Sample Method Annotation