ssm动态切换数据源:
自定义一个类继承spring的AbstractRoutingDataSource覆盖determineCurrentLookupKey方法
package com.tzc.webapi.dbRouting;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import java.util.Map;
public class DynamicDataSource extends AbstractRoutingDataSource {
//查找用户上下文变量中设置的数据源
@Override
protected Object determineCurrentLookupKey() {
// TODO Auto-generated method stub
return DataSourceContextHolder.getDataSourceType();
}
//设置默认数据源
@Override
public void setDefaultTargetDataSource(Object defaultTargetDataSource) {
super.setDefaultTargetDataSource(defaultTargetDataSource);
}
//设置数据源集合
@Override
public void setTargetDataSources(Map targetDataSources) {
super.setTargetDataSources(targetDataSources);
}
}
上下文环境的类:
package com.tzc.webapi.dbRouting;
public class DataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal();
public static void setDataSourceType(String dataSourceType) {
contextHolder.set(dataSourceType);
}
public static String getDataSourceType() {
return contextHolder.get();
}
public static void clearDataSourceType() {
contextHolder.remove();
}
}
spring.xml配置多个数据源eg:
<bean id="masterDataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${master.jdbc.driverClassName}" />
<property name="url" value="${master.jdbc.url}" />
<property name="username" value="${master.jdbc.username}" />
<property name="password" value="${master.jdbc.password}" />
<!-- 配置初始化大小、最小、最大 -->
<property name="initialSize" value="${initialSize}" />
<property name="minIdle" value="${minIdle}" />
<property name="maxActive" value="${maxActive}" />
<!-- 配置获取连接等待超时的时间 -->
<property name="maxWait" value="${maxWait}" />
</bean>
<bean id="slaveDataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${slave.jdbc.driverClassName}" />
<property name="url" value="${slave.jdbc.url}" />
<property name="username" value="${slave.jdbc.username}" />
<property name="password" value="${slave.jdbc.password}" />
<!-- 配置初始化大小、最小、最大 -->
<property name="initialSize" value="${initialSize}" />
<property name="minIdle" value="${minIdle}" />
<property name="maxActive" value="${maxActive}" />
<!-- 配置获取连接等待超时的时间 -->
<property name="maxWait" value="${maxWait}" />
</bean>
<bean id="dataSource" class="com.tzc.webapi.dbRouting.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="master" value-ref="masterDataSource"/>
<entry key="slave" value-ref="slaveDataSource"/>
</map>
</property>
<property name="defaultTargetDataSource" ref="masterDataSource"></property>
</bean>
④基于自定义注解+aop实现数据源的动态切换eg:
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
String value() default "";
}
#aop实现类
@Aspect
@Component
@Order(1)
public class DynamicDataSourceAspect {
private static Logger logger = org.slf4j.LoggerFactory.getLogger(DynamicDataSourceAspect.class);
@Pointcut("@within(com.tzc.webapi.dbRouting.DataSource)||@annotation(com.tzc.webapi.dbRouting.DataSource)")
public void pointCut() {}
@Before("pointCut()")
public void before(JoinPoint point) {
logger.info("切面捕获到修改数据源信息");
MethodSignature signa = (MethodSignature) point.getSignature();
Method method = signa.getMethod();
DataSource annotationClass = method.getAnnotation(DataSource.class);//获取方法上的注解
if(annotationClass == null){
annotationClass = point.getTarget().getClass().getAnnotation(DataSource.class);//获取类上面的注解
if(annotationClass == null) return;
}
//获取注解上的数据源的值的信息
String dataSourceKey = annotationClass.value();
System.out.println(dataSourceKey+"已经切入");
if(dataSourceKey !=null){
//给当前的执行SQL的操作设置特殊的数据源的信息
DataSourceContextHolder.setDataSourceType(dataSourceKey);
}
logger.info("AOP动态切换数据源,className"+point.getTarget().getClass().getName()+"methodName"+method.getName()+";dataSourceKey:"+dataSourceKey==""?"默认数据源":dataSourceKey);
}
@After("pointCut()")
public void after(JoinPoint point){
DataSourceContextHolder.clearDataSourceType();
}
}
#开启aop自动代理
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
#注入bean
<bean name="myAspectJ" class="com.tzc.webapi.dbRouting.DynamicDataSourceAspect"/>
业务层
@DataSource("master")
public User getUserMasterById(Long id) {
return userMapper.selectByPrimaryKey(id);
}
@DataSource("slave")
public User getUserSlaveById(Long id) {
return userMapper.selectByPrimaryKey(id);
}
@DataSource("master")
public void saveMaster(User user) {
userMapper.insert(user);
}
@DataSource("slave")
public void saveSlave(User user) {
userMapper.insert(user);
}
public List<User> getUsers() {
List<User> users = userMapper.selectUsers();
return users;
}
测试
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.tzc.webapi.bean.Book;
import com.tzc.webapi.bean.User;
import com.tzc.webapi.service.BookService;
import com.tzc.webapi.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({ "classpath:spring-mybatis.xml" })
public class UserServiceTest {
@Autowired
private UserService userService;
@Autowired
private BookService bookService;
private ExecutorService service = Executors.newFixedThreadPool(20);
@Test
public void testQuery() {
User masterUser = userService.getUserMasterById(1L);
System.out.println(masterUser);
System.out.println("-------------------------------分割线---------------------------------");
User slaveUser = userService.getUserSlaveById(1L);
System.out.println(slaveUser);
}
@Test
public void testQueryThread() {
for (int i = 0; i < 20; i++) {
if (i % 2 == 0) {
service.execute(new Runnable() {
@Override
public void run() {
User masterUser = userService.getUserMasterById(1L);
System.out.println(masterUser);
}
});
} else {
service.execute(new Runnable() {
@Override
public void run() {
User slaveUser = userService.getUserSlaveById(1L);
System.out.println(slaveUser);
}
});
}
}
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
service.shutdown();
}
@Test
public void testSave() {
User master = new User();
master.setAge(0);
master.setPassword("master");
master.setUserName("主机1");
userService.saveMaster(master);
User slave = new User();
slave.setAge(100);
slave.setPassword("slave");
slave.setUserName("备机1");
userService.saveSlave(slave);
}
@Test
public void testTrans() {
User master = new User();
master.setAge(111);
master.setPassword("1111");
master.setUserName("testTrans");
userService.saveMaster(master);
}
@Test
public void testDifTableQuery() {
for (int i = 0; i < 20; i++) {
if (i % 2 == 0) {
service.execute(new Runnable() {
@Override
public void run() {
Book book = bookService.getBookById(1L);
System.out.println(book);
}
});
} else {
service.execute(new Runnable() {
@Override
public void run() {
User slaveUser = userService.getUserSlaveById(1L);
System.out.println(slaveUser);
}
});
}
}
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
service.shutdown();
}
}
总结:其实就是在操作的方法的自定义注解上拿到value值(也就是数据源名称),然后传给spring路由的那个覆盖的lookup方法就ok了。