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

spring+myBatisi实现多数据源动态切换

2017-05-18 13:29 627 查看
        目前网上实现动态数据源的文章很多,基本都是根据spring的AOP实现的,本来以为简单,复制粘贴即可,结果遇到不少问题,一是遇到AOP与事务之间执行顺序的问题,二是service实现类遇到注解无法获取的问题,前一个问题使用order解决了,后一个问题则是放到service接口类中就可以获取。具体如下:

1、自定义注解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* RUNTIME
* 编译器将把注释记录在类文件中,在运行时 VM 将保留注释,因此可以反射性地读取。
* @author yangGuang
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DataSource {
String value();
}

2、基于spring的aop实现多数据源(读和写两个数据源):

1)写一个ChooseDataSource: 类继承AbstractRoutingDataSource,并实现determineCurrentLookupKey方法:

1
2
3
4
5
6
7
8
9
10

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class ChooseDataSource extends AbstractRoutingDataSource {

@Override
protected Object determineCurrentLookupKey() {
return HandleDataSource.getDataSource();
}

}

2)利用ThreadLocal解决线程安全问题:
1
2
3
4
5
6
7
8
9
10

public class HandleDataSource {
public static final ThreadLocal<String> holder = new ThreadLocal<String>();
public static void putDataSource(String datasource) {
holder.set(datasource);
}

public static String getDataSource() {
return holder.get();
}
}

3)定义一个数据源切面类,通过aop访问,获取方法上的自定义注解,然后根据注解内容尽情判断,动态设置数据源:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1516
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

import java.lang.reflect.Method;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
//@Aspect
//@Component
public class DataSourceAspect {
//@Pointcut("execution(* com.apc.cms.service.*.*(..))")
public void pointCut(){};

//  @Before(value = "pointCut()")
public void before(JoinPoint point)
{
Object target = point.getTarget();
System.out.println(target.toString());
String method = point.getSignature().getName();
System.out.println(method);
Class<?>[] classz = target.getClass().getInterfaces();
Class<?>[] parameterTypes = ((MethodSignature) point.getSignature())
.getMethod().getParameterTypes();
try {
Method m = classz[0].getMethod(method, parameterTypes);
System.out.println(m.getName());
if (m != null && m.isAnnotationPresent(DataSource.class)) {
DataSource data = m.getAnnotation(DataSource.class);
HandleDataSource.putDataSource(data.value());
}

} catch (Exception e) {
e.printStackTrace();
}
}
}

4)配置applicationContext.xml:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
1516
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91

<!-- 主库数据源 -->
<bean id="writeDataSource" class="com.jolbox.bonecp.BoneCPDataSource"  destroy-method="close">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://172.22.14.6:3306/cpp?autoReconnect=true"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
<property name="partitionCount" value="4"/>
<property name="releaseHelperThreads" value="3"/>
<property name="acquireIncrement" value="2"/>
<property name="maxConnectionsPerPartition" value="40"/>
<property name="minConnectionsPerPartition" value="20"/>
<property name="idleMaxAgeInSeconds" value="60"/>
<property name="idleConnectionTestPeriodInSeconds" value="60"/>
<property name="poolAvailabilityThreshold" value="5"/>
</bean>

<!-- 从库数据源 -->
<bean id="readDataSource" class="com.jolbox.bonecp.BoneCPDataSource"  destroy-method="close">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://172.22.14.7:3306/cpp?autoReconnect=true"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
<property name="partitionCount" value="4"/>
<property name="releaseHelperThreads" value="3"/>
<property name="acquireIncrement" value="2"/>
<property name="maxConnectionsPerPartition" value="40"/>
<property name="minConnectionsPerPartition" value="20"/>
<property name="idleMaxAgeInSeconds" value="60"/>
<property name="idleConnectionTestPeriodInSeconds" value="60"/>
<property name="poolAvailabilityThreshold" value="5"/>
</bean>

<!-- transaction manager, 事务管理 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>

<!-- 注解自动载入 -->
<context:annotation-config />

<!--enale component scanning (beware that this does not enable mapper scanning!)-->
<context:component-scan base-package="com.apc.cms.persistence.rdbms" />
<context:component-scan base-package="com.apc.cms.service">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Component" />
</context:component-scan>

<context:component-scan base-package="com.apc.cms.auth" />

<!-- enable transaction demarcation with annotations -->
<tx:annotation-driven />

<!-- define the SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="typeAliasesPackage" value="com.apc.cms.model.domain" />
</bean>

<!-- scan for mappers and let them be autowired -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.apc.cms.persistence" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>

<bean id="dataSource" class="com.apc.cms.utils.ChooseDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<!-- write -->
<entry key="write" value-ref="writeDataSource"/>
<!-- read -->
<entry key="read" value-ref="readDataSource"/>
</map>

</property>
<property name="defaultTargetDataSource" ref="writeDataSource"/>
</bean>

<!-- 激活自动代理功能 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>

<!-- 配置数据库注解aop -->
<bean id="dataSourceAspect" class="com.apc.cms.utils.DataSourceAspect" />
<aop:config>
<aop:aspect id="c" ref="dataSourceAspect">
<aop:pointcut id="tx" expression="execution(* com.apc.cms.service..*.*(..))"/>
<aop:before pointcut-ref="tx" method="before"/>
</aop:aspect>
</aop:config>
<!-- 配置数据库注解aop -->

3、测试,如果是在service中实现,需要放到接口类中,放到实现类中无法获取注解。

详情可见:http://www.iteye.com/problems/58681
public interface TestService {
@DataSource("write")
Document update(User user) throws Exception;
@DataSource("read")
Document getDocById(long id) throws Exception;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: