版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_37279783/article/details/82013702
这是实际应用场景中的多数据源切换案例
逻辑思路如下:
1.系统初始化,加载所有数据库中配置的数据源,加载进去spring容器
2.通过两种方法切换数据源:
2.1MultipleDataSource.setDataSourceKey(dataSourceKey);//切换
MultipleDataSource.clearDataSourceKey();//清除当前数据源并且还原到默认数据库
2.2使用@SwitchDataSource(name="dateSourceKey")注解
<-- 配置默认数据源 -->
import cn.commons.string.StringUtils;
import cn.core.model.datasource.DataSource;
import cn.core.model.logger.DruidSQLLoggerMonitorAppender;
import cn.core.persistent.hibernate.AbstractHibernateDao;
import cn.core.persistent.hibernate.extend.BaseExtendDao;
import com.alibaba.druid.pool.DruidDataSource;
import org.apache.commons.collections.map.HashedMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Query;
import org.hibernate.Session;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.util.Assert;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* 多数据源加载
* @bug [nothing]
* @see [nothing]
* @备注:
*/
public class LoadDataSource extends AbstractHibernateDao {
protected final Log logger = LogFactory.getLog(getClass());
private BaseExtendDao baseExtendDao;
/**
* 加载数据库内配置的数据源
* */
public List
Session session = null;
try {
session = super.getSessionFactory().openSession();
//查询数据库中所有数据源数据
Query query = session.getNamedQuery("DataSource.findAll");
return query.list();
} catch (Exception e) {
} finally {
if (session != null) {
session.flush();
session.close();
}
}
return new ArrayList();
}
/**
* 创建数据源并注入进 spring 容器
* */
public void buiderDataSourceToSpring(ApplicationContext applicationContext) {
//加载数据源
//根据数据源,创建 DruidDataSource.class
//重新创建多数据源
List
if (listDataSource.size() > 0) {
//默认数据源bean的id
DruidDataSource druidDataSourceDefault = applicationContext.getBean(SwitchDataSource.master, DruidDataSource.class);
Map
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SwitchDataSource {
String master = "dataSource";
String dataSourceKey() default SwitchDataSource.master;
}
import cn.commons.string.StringUtils;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.*;
/**
* 多数据源切换
* @bug [nothing]
* @see [nothing]
* @备注:
*/
public class MultipleDataSource extends AbstractRoutingDataSource implements MethodInterceptor {
//保存当前线程使用的数据源
private static final ThreadLocal
//设置数据源
public static void setDataSourceKey(String dataSourceName) {
if (StringUtils.hasText(dataSourceName)) {
MultipleDataSource.dataSourceKey.set(dataSourceName);
}
}
//清除数据源,清除后使用默认的支撑库数据源
public static void clearDataSourceKey() {
MultipleDataSource.dataSourceKey.remove();
}
/**
*
* sessionFactory 加载数据源扩展方法
* 详情请看 AbstractRoutingDataSource.determineTargetDataSource() 方法;
* 这是一个扩展,返回的是数据源的名称,也就是 spring 配置的 baenName,通过返回的 beanName未找到数据源会直接使用支撑库做为数据源
* */
@Override
protected Object determineCurrentLookupKey() {
//这里返回 null或"" 没有关系,在 bean-core.xml 我们注入了默认数据源
return MultipleDataSource.dataSourceKey.get();
}
/**
* 结合 spring aop 与 annotation 动态切换数据源.
* 框架通过 Aop 实现了事务管理,那么数据源的切换与事务控制作用于相同的时候方法上,并且优先于事务控制切换数据源.详细配置请参考 beans-core.xml
*
* 如何使用:
* 在需要切换数据源的service层的方法上,配置注解标签,并指定数据源名称,代码如下:
*
* @SwitchDataSource(name="dateSourceKey")
* public List getProducts(){
* ...
* }
*
*/
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Object result = null;
SwitchDataSource switchDataSource = null;
try {
switchDataSource = invocation.getMethod().getAnnotation(SwitchDataSource.class);//接口方法定义的注解
if (switchDataSource == null) {
// Class[] argsClass = new Class[invocation.getArguments().length];
// for (int i = 0, j = invocation.getArguments().length; i < j; i++) {
// argsClass[i] = invocation.getArguments()[i].getClass();
// }
//上面的获取方式有可能来至于接口,这里尝试在实现类获取注解
// switchDataSource = invocation.getThis().getClass().getMethod(invocation.getMethod().getName(), argsClass).getAnnotation(SwitchDataSource.class);
// 20170526 修改
// 在接口中定义方法时,一般的形参都会实现父类或者接口.而实现类传入的实参又会是具体的实现类.所以导致直接使用 getMethod(String name, Class>... parameterTypes) 是无法获取方法的.
//尝试在实现类获取注解
String methodName = invocation.getMethod().getName();
Object[] args = invocation.getArguments();//实际参数;如 arraylist
Method[] methods = invocation.getThis().getClass().getMethods();
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName().equals(methodName)) {//方法相同
Class[] typeClasss = methods[i].getParameterTypes();//定义的参数类型;如 list
if (typeClasss.length == args.length) {//参数数量相等
if (typeClasss.length == 0) {//无参情况
switchDataSource = methods[i].getAnnotation(SwitchDataSource.class);
break;
} else {
boolean isTrue = false;
//判断所有的参数的类型是否相同
for (int j = 0; j < args.length; j++) {
if (typeClasss[j].isAssignableFrom(args[j].getClass())) {
isTrue = true;
} else {
isTrue = false;
break;
}
}
if (isTrue) {
switchDataSource = methods[i].getAnnotation(SwitchDataSource.class);
break;
}
}
}
}
}
}
if (switchDataSource != null) {
MultipleDataSource.setDataSourceKey(switchDataSource.dataSourceKey());
}
result = invocation.proceed();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
if (switchDataSource != null) {
MultipleDataSource.clearDataSourceKey();//还原数据源;
}
}
return result;
}
public static void main(String[] args) {
System.out.println(ArrayList.class.isAssignableFrom(List.class));
}
}
//系统初始化加载数据库中的数据源
import javax.servlet.ServletException;
import cn.core.persistent.hibernate.datasource.LoadDataSource;
import cn.core.persistent.hibernate.datasource.MultipleDataSource;
import org.apache.commons.collections.map.HashedMap;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
*
* 提供框架启动初始化扩展功能,继承 DispatcherServlet 并实现 initFrameworkServlet() 方法,该方法原生提供 spring 扩展
*
* 目前提供扩展如下:
* 1.加载多数据源
* @类名称:RapidFrameWorkServlet
*/
public class RapidFrameWorkServlet extends DispatcherServlet {
private static final long serialVersionUID = 1L;
/**
* 覆写spring的一个方法用来初始化进而得到初始化参数
*/
@Override
public void initFrameworkServlet() throws ServletException {
try {
//加载多数据源
super.getWebApplicationContext().getBean("loadDataSource", LoadDataSource.class).buiderDataSourceToSpring(super.getWebApplicationContext());
} catch (Exception e) {
e.printStackTrace();
}
}
}
<-- 数据库中数据源对象 -->
CREATE TABLE DATASOURCE
(
DATASOURCE_GUID VARCHAR2(32 BYTE),
DATASOURCE_NAME VARCHAR2(128 BYTE),
DATASOURCE_KEY VARCHAR2(128 BYTE) NOT NULL,
DRIVER_CLASS_NAME VARCHAR2(256 BYTE) NOT NULL,
URL VARCHAR2(512 BYTE) NOT NULL,
USERNAME VARCHAR2(128 BYTE) NOT NULL,
PASSWORD VARCHAR2(128 BYTE) NOT NULL,
CONNECT_POOL_PROPERTIES VARCHAR2(1024 BYTE),
OPERATOR_ID VARCHAR2(64 BYTE),
CREATE_TIME DATE,
MODIFY_TIME DATE,
RES_ACT_MAP_ID VARCHAR2(64 BYTE),
VALID_SIGN NUMBER
)
//数据源控制层类,在新增数据源后重新加载容器的数据源
public class DataSourceResource implements ApplicationContextAware {
@Resource
private IDataSourceService dataSourceService;
@Resource(name="loadDataSource")
private LoadDataSource loadDataSource;
private ApplicationContext applicationContext;
public void setApplicationContext(ApplicationContext paramApplicationContext) throws BeansException {
applicationContext=paramApplicationContext;
}
/**
* 添加数据
* @param dataSourceType
* @return
*/
@RequestMapping("/put")
@ResponseBody
@RepeatSubmitToken
public boolean addDataSourceTypeAction(DataSource dataSource){
boolean flag= dataSourceService.selectDataSourceName(dataSourceType.getDataSourceName());
if(flag){
return false;
}
boolean isSuccess = dataSourceService.addDataSourceType(dataSource);
//数据源添加成功后重新加载数据源
loadDataSource.buiderDataSourceToSpring(applicationContext);
return isSuccess;
}
}
————————————————
版权声明:本文为CSDN博主「小蜗牛的路」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_37279783/article/details/82013702