版权声明:本文为博主原创文章,遵循 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")注解

<beans>
<-- 配置默认数据源 -->
<context:property-placeholder location="classpath*:jdbc.properties"/>

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>

<property name="minIdle" value="${rapid.jdbcPool.minPoolSize}"/>
<property name="maxActive" value="${rapid.jdbcPool.maxPoolSize}"/>
<property name="initialSize"
value="${rapid.jdbcPool.initialPoolSize}"/><!-- 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时,default=0 -->
<property name="maxWait"
value="${rapid.jdbcPool.maxWait}"/><!-- 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁 -->

<property name="timeBetweenEvictionRunsMillis"
value="${rapid.jdbcPool.timeBetweenEvictionRunsMillise}"/><!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,毫秒 -->
<property name="minEvictableIdleTimeMillis"
value="${rapid.jdbcPool.minEvictableIdleTimeMillis}"/><!-- 配置一个连接在池中最小生存的时间,毫秒 -->

<!-- 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于 timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效 -->
<property name="testWhileIdle" value="${rapid.jdbcPool.testWhileIdle}"/>
<property name="testOnBorrow" value="false"/>
<property name="testOnReturn" value="false"/>

<!-- 用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都无效 -->
<property name="validationQuery" value="${rapid.jdbcPool.validationQuery}"/>

<!-- #是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭,default=false -->
<property name="poolPreparedStatements" value="${rapid.jdbcPool.poolPreparedStatements}"/>
<!-- #要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。
#在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100 -->
<property name="maxOpenPreparedStatements" value="${rapid.jdbcPool.maxOpenPreparedStatements}"/>

<!-- #属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有:
监控统计用的filter:stat
日志用的filter:log4j
防御sql注入的filter:wall -->
<!--<property name="filters" value="stat"/>-->
<property name="timeBetweenLogStatsMillis"
value="${rapid.jdbcPool.timeBetweenLogStatsMillis}"/><!-- 定时把监控数据输出到日志中,毫秒 -->
<property name="proxyFilters">
<list>
<ref bean="stat-filter"/>
<ref bean="log-filter"/>
</list>
</property>
</bean>

<!-- 多数据源切换 -->
<bean id="multipleDataSource" class="cn.core.persistent.hibernate.datasource.MultipleDataSource">
<property name="defaultTargetDataSource" ref="dataSource"/>
<property name="targetDataSources">
<map>
<entry key="dataSource" value-ref="dataSource"/>
</map>
</property>
</bean>

<!-- 多数据源注入 -->
<bean id="loadDataSource" class="cn.core.persistent.hibernate.datasource.LoadDataSource">
<property name="baseExtendDao" ref="baseExtendDao"/>
</bean>
<!-- 声明事务 -->
<bean id="txManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="dataSource" ref="multipleDataSource"/>
<property name="sessionFactory" ref="sessionFactory"/>
</bean>

<aop:config>
<aop:pointcut id="myMethod" expression="execution(* cn.*.service..*.*(..))"/>
<aop:advisor advice-ref="multipleDataSource" order="1"
pointcut="execution(* cn.*.service..*.*(..)) and @annotation(cn.core.persistent.hibernate.datasource.SwitchDataSource)"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="myMethod" order="2"/>
</aop:config>

<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="append*" propagation="REQUIRED"/>
<tx:method name="insert*" propagation="REQUIRED"/>
<tx:method name="save*" propagation="REQUIRED"/>

<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="remove*" propagation="REQUIRED"/>
<tx:method name="repair*" propagation="REQUIRED"/>
<tx:method name="delAndRepair*" propagation="REQUIRED"/>

<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="modify*" propagation="REQUIRED"/>
<tx:method name="edit*" propagation="REQUIRED"/>

<tx:method name="init*" propagation="REQUIRED"/>
<tx:method name="test*" propagation="REQUIRED"
rollback-for="Exception"/><!-- 如果不写 rollback-for它弄死都不给你回滚,这个东西弄了半天 -->

<tx:method name="query*" propagation="SUPPORTS" read-only="true"/>
<tx:method name="select*" propagation="SUPPORTS" read-only="true"/>
<tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
<tx:method name="load*" propagation="SUPPORTS" read-only="true"/>
<tx:method name="search*" propagation="SUPPORTS" read-only="true"/>

<!--指定当前方法以非事务方式执行操作,如果当前存在事务,就把当前事务挂起,等我以非事务的状态运行完,再继续原来的事务。查询定义即可 read-only="true" 表示只读-->
<tx:method name="*" propagation="NOT_SUPPORTED" read-only="true"/>

<!--<tx:method name="logical*" propagation="REQUIRED" />&lt;!&ndash; 一般指逻辑删除 &ndash;&gt;-->
<!--<tx:method name="batch*" propagation="REQUIRED" />&lt;!&ndash; 批量操作 &ndash;&gt;-->
</tx:attributes>
</tx:advice>
</beans>

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<DataSource> selectDataSourceForDateBase() {
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<DataSource> listDataSource = this.selectDataSourceForDateBase();

if (listDataSource.size() > 0) {
//默认数据源bean的id
DruidDataSource druidDataSourceDefault = applicationContext.getBean(SwitchDataSource.master, DruidDataSource.class);

Map<Object, Object> targetDataSources = new HashedMap();
targetDataSources.put(SwitchDataSource.master, druidDataSourceDefault);//支撑库

//Bean的实例工厂
DefaultListableBeanFactory dbf = (DefaultListableBeanFactory) ((ConfigurableApplicationContext) applicationContext).getBeanFactory();

for (Iterator<DataSource> iterator = listDataSource.iterator(); iterator.hasNext(); ) {
DataSource ds = iterator.next();

//Bean构建 BeanService.class 要创建的Bean的Class对象
BeanDefinitionBuilder dataSourceBuider = BeanDefinitionBuilder.genericBeanDefinition(DruidDataSource.class);

//向里面的属性注入值,提供get set方法
toolBuiderDataSourceToSpring(dataSourceBuider, ds);

dataSourceBuider.setInitMethodName("init");
dataSourceBuider.setDestroyMethodName("close");

//将实例注册spring容器中 key 等同于 id配置
dbf.registerBeanDefinition(ds.getDataSourceKey(), dataSourceBuider.getBeanDefinition());

DruidDataSource druidDataSource = applicationContext.getBean(ds.getDataSourceKey(), DruidDataSource.class);
targetDataSources.put(ds.getDataSourceKey(), druidDataSource);

logger.info("加载数据源:名称->[" + ds.getDataSourceKey() + "],url->[" + druidDataSource.getUrl() + "],driver->[" + druidDataSource.getDriverClassName() + "],username->[" + druidDataSource.getUsername() + "]");
}

//重构多数据源列表
MultipleDataSource multipleDataSource = applicationContext.getBean("multipleDataSource", MultipleDataSource.class);
multipleDataSource.setTargetDataSources(targetDataSources);
multipleDataSource.afterPropertiesSet();
}
}

/**
* 数据库配置的数据源注入容器
*
* @title toolBuiderDataSourceToSpring
* @param dataSourceBuider spring 容器注入实例
* ds 数据库配置的数据源
* @return
* @throws
*/
private void toolBuiderDataSourceToSpring(BeanDefinitionBuilder dataSourceBuider, DataSource ds) {
Assert.notNull(ds, "多数据源注入异常 -> 配置的数据源不能为 null!!");
Assert.notNull(dataSourceBuider, "多数据源注入异常 -> Spring 容器为 null!!");

Assert.hasText(ds.getDriverClassName(), "多数据源加载异常 -> [" + ds.getDataSourceKey() + "] -> [driverClassName is null]");
Assert.hasText(ds.getUrl(), "多数据源加载异常 -> [" + ds.getDataSourceKey() + "] -> [url is null]");
Assert.hasText(ds.getUsername(), "多数据源加载异常 -> [" + ds.getDataSourceKey() + "] -> [username is null]");
Assert.hasText(ds.getPassword(), "多数据源加载异常 -> [" + ds.getDataSourceKey() + "] -> [password is null]");

dataSourceBuider.addPropertyValue("driverClassName", ds.getDriverClassName());
dataSourceBuider.addPropertyValue("url", ds.getUrl());
dataSourceBuider.addPropertyValue("username", ds.getUsername());
dataSourceBuider.addPropertyValue("password", ds.getPassword());

if (StringUtils.hasText(ds.getConnectPoolProperties())) {
Map map = StringUtils.parseStringToMap(ds.getConnectPoolProperties(), ",", "=");

if (map.containsKey("minPoolSize")) {//最小连接
dataSourceBuider.addPropertyValue("minIdle", map.get("minPoolSize"));
}

if (map.containsKey("maxPoolSize")) {//最大连接
dataSourceBuider.addPropertyValue("maxActive", map.get("maxPoolSize"));
}

if (map.containsKey("initialPoolSize")) {//初始连接数
dataSourceBuider.addPropertyValue("initialSize", map.get("initialPoolSize"));
}

if (map.containsKey("maxWait")) {//获取连接时最大等待时间
dataSourceBuider.addPropertyValue("maxWait", map.get("maxWait"));
}

if (map.containsKey("timeBetweenEvictionRunsMillis")) {//配置间隔多久才进行一次检测,检测需要关闭的空闲连接,毫秒
dataSourceBuider.addPropertyValue("timeBetweenEvictionRunsMillis", map.get("timeBetweenEvictionRunsMillis"));
}

if (map.containsKey("minEvictableIdleTimeMillis")) {//配置一个连接在池中最小生存的时间,毫秒
dataSourceBuider.addPropertyValue("minEvictableIdleTimeMillis", map.get("minEvictableIdleTimeMillis"));
}

//是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭,default=false
if (map.containsKey("poolPreparedStatements")) {
dataSourceBuider.addPropertyValue("poolPreparedStatements", map.get("poolPreparedStatements"));
}

//#要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。
//#在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100
if (map.containsKey("maxOpenPreparedStatements")) {
dataSourceBuider.addPropertyValue("maxOpenPreparedStatements", map.get("maxOpenPreparedStatements"));
}

//用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都无效
if (map.containsKey("validationQuery")) {
String vq = (String) map.get("validationQuery");
if (StringUtils.hasText(vq)) {
dataSourceBuider.addPropertyValue("validationQuery", map.get("validationQuery"));

//建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于 timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效
if (map.containsKey("testWhileIdle")) {
dataSourceBuider.addPropertyValue("testWhileIdle", map.get("testWhileIdle"));
}

if (map.containsKey("testOnBorrow")) {
dataSourceBuider.addPropertyValue("testOnBorrow", map.get("testOnBorrow"));
}

if (map.containsKey("testOnReturn")) {
dataSourceBuider.addPropertyValue("testOnReturn", map.get("testOnReturn"));
}
}
}

//是否日志打印
if (map.containsKey("statementExecutableSqlLogEnable")) {
boolean isStatementExecutableSqlLogEnable = (Boolean) map.get("statementExecutableSqlLogEnable");
if (isStatementExecutableSqlLogEnable) {
DruidSQLLoggerMonitorAppender log4jFilter = new DruidSQLLoggerMonitorAppender();
log4jFilter.setStatementExecutableSqlLogEnable(true);

if (map.containsKey("serializationCurrentThreadExecutableSqlLogEnable")) {
log4jFilter.setSerializationCurrentThreadExecutableSqlLogEnable((Boolean) map.get("serializationCurrentThreadExecutableSqlLogEnable"));
}

List list = new ArrayList();
list.add(log4jFilter);
dataSourceBuider.addPropertyValue("proxyFilters", list);
}
}
}
}

public BaseExtendDao getBaseExtendDao() {
return baseExtendDao;
}

public void setBaseExtendDao(BaseExtendDao baseExtendDao) {
this.baseExtendDao = baseExtendDao;
}

}

@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<String> dataSourceKey = new InheritableThreadLocal();

//设置数据源
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层的方法上,配置注解标签,并指定数据源名称,代码如下:
* <code>
* @SwitchDataSource(name="dateSourceKey")
* public List getProducts(){
* ...
* }
* </code>
*/
@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

多数据源切换-Druid的更多相关文章

  1. 基于springboot通过注解AOP动态切换druid多数据源--mybatis

    控制于接口之上: 开始:demo地址  在lsr-core-base中 自定义注解: /** * @Description: 数据源切换注解 * @Package: lsr-microservice ...

  2. SpringBoot框架:通过AOP和自定义注解完成druid连接池的动态数据源切换(三)

    一.引入依赖 引入数据库连接池的依赖--druid和面向切面编程的依赖--aop,如下所示: <!-- druid --> <dependency> <groupId&g ...

  3. AbstractRoutingDataSource实现动态数据源切换 专题

    需求:系统中要实现切换数据库(业务数据库和his数据库) 网上很多资料上有提到AbstractRoutingDataSource,大致是这么说的 在Spring 2.0.1中引入了AbstractRo ...

  4. SpringMVC+ Mybatis 配置多数据源 + 自动数据源切换 + 实现数据库读写分离

    现在大型的电子商务系统,在数据库层面大都采用读写分离技术,就是一个Master数据库,多个Slave数据库.Master库负责数据更新和实时数据查询,Slave库当然负责非实时数据查询.因为在实际的应 ...

  5. spring mvc 多数据源切换,不支持事务控制[一]

    一个项目中需要使用两个数据库,Oracle 和Mysql ,于是参考各个blog,实现此功能.写好后才发现,原来的事务失效了,我去... spring-mybatis.xml 配置 <bean ...

  6. springboot(2.0以上) --数据源切换时报错

    在进行数据源切换时spring.datasource.type类型根据源码所给的默认值修改后依然报错 先看源码:标色部分 ,  就是springboot所给的数据源 ,  正常来说只要在配置文件中修改 ...

  7. 【开发笔记】- AbstractRoutingDataSource动态数据源切换,AOP实现动态数据源切换

    AbstractRoutingDataSource动态数据源切换 上周末,室友通宵达旦的敲代码处理他的多数据源的问题,搞的非常的紧张,也和我聊了聊天,大概的了解了他的业务的需求.一般的情况下我们都是使 ...

  8. Spring中AOP方式实现多数据源切换

    作者:suroot spring动态配置多数据源,即在大型应用中对数据进行切分,并且采用多个数据库实例进行管理,这样可以有效提高系统的水平伸缩性.而这样的方案就会不同于常见的单一数据实例的方案,这就要 ...

  9. SpringBoot学习笔记:动态数据源切换

    SpringBoot学习笔记:动态数据源切换 数据源 Java的javax.sql.DataSource接口提供了一种处理数据库连接的标准方法.通常,DataSource使用URL和一些凭据来建立数据 ...

随机推荐

  1. 百度地图根据list经纬度算每个点到剩余点的平均距离、最远距离和最近距离

    一.使用步骤 说明:给你一大串坐标list.计算每个点到其他所有点的最近最远平均距离. 1.打开百度地图api在线demo(随便一个都行) 2.替换<script></script& ...

  2. 我的翻译--一个针对TP-Link调试协议(TDDP)漏洞挖掘的故事

    前言 我写这篇文章原本是为了简化WiFi渗透测试研究工作.我们想使用去年由Core Security发布的WIWO,它可以在计算机网络接口和WiFi路由器之间建立一个透明的通道. 研究的第一步,就是选 ...

  3. maven依赖报红的一些解决办法

    使用IDEA集成maven管理项目依赖时,经常出现更改pom文件后maven依赖列表并未更改,且依赖报红,此时建议检查maven配置文件maven仓库是否下载好jar包,如果未下载好jar包文件夹内会 ...

  4. Git常用命令简记

    创建仓库 添加需要版本控制的文件到仓库中 提交到版本库 修改位于顶端的commit的日志 分支管理 版本回退 切换与合并分支 本地版本库与远程关联 克隆 Tag的使用 问题与解决 创建git仓库 gi ...

  5. 【转】IEnumerable接口的一个简单示例

    转:https://blog.csdn.net/wang371756299/article/details/8197075 摘要: IEnumerable接口是实现foreach循环的一个重要的接口, ...

  6. LeetCode Continuous Subarray Sum 题解 同余前缀和 Hash表

    文章目录 题意 思路 特殊情况k=0 Source Code 1 Source Code 2 题意 给定一个数组和一个整数k,返回是否存在一个长度至少为2的连续子数组的和为k的倍数. 思路 和上一篇博 ...

  7. 仿ios按钮切换

    <div> <label><input class="btn-switch" type="checkbox"> 默认未选中& ...

  8. 题解【洛谷P1896】[SCOI2005]互不侵犯

    题面 棋盘类状压 DP 经典题. 我们考虑设 \(dp_{i,j,s}\) 表示前 \(i\) 行已经摆了 \(j\) 个国王,且第 \(i\) 行国王摆放的状态为 \(s\) 的合法方案数. 转移的 ...

  9. 2020牛客寒假算法基础集训营6 I.导航系统 (最小生成树)

    https://ac.nowcoder.com/acm/contest/3007/I 题中给定的图必定是一棵树 容易发现,如果将输入的N(N-1)个距离看做N(N-1)条无向边的话,那么如果数据合法, ...

  10. leetcode腾讯精选练习之相交链表(六)

    相交链表 题目: 编写一个程序,找到两个单链表相交的起始节点. 如下面的两个链表: 在节点 c1 开始相交. 示例 1: 输入:intersectVal = 8, listA = [4,1,8,4,5 ...