SpringMVC配置多个数据源

news/2024/4/19 10:19:28/文章来源:https://blog.csdn.net/pengjinlong521/article/details/129240974

简介

今天遇到一个需求,我们开发的项目需要同时连接 DB2 和 SQLite 数据库,刚开始认为可以复制一份当前数据源的配置就可以了,结果发现失败了;原因是:SqlSessionFactory 只能是单例模式,所以根本无法通过这种方式实现多数据源的开发

解决方案:手动创建一个动态的数据源,将 DB2 和 SQlite 数据源放入,再将该动态数据源放入 ThreadLocal,然后在代码中指定需要的数据源即可

实现步骤

1. 新建 DynamicDataSource 类,添加如下代码

/*** @description dynamic dataSource:sqlite and db2*/
public class DynamicDataSource extends AbstractRoutingDataSource {public static final ThreadLocal < String > contextHolder = new ThreadLocal < String > ();public static final String DBTYPE_SQLITE = "sqlite";public static final String DBTYPE_DB2 = "db2";public static void setDBType(String customerType) {contextHolder.set(customerType);}public static String getDBType() {return contextHolder.get();}public static void clearDBType() {contextHolder.remove();}// 返回目标数据库@Overrideprotected DataSource determineTargetDataSource() {return super.determineTargetDataSource();}@Overrideprotected Object determineCurrentLookupKey() {return getDBType();}
}

2. ApplicationConfig 配置类关于数据源配置如下

/*** @author cddufu@cn.ibm.com* @date 2018-08-21* @description dynamic dataSource:sqlite and db2*/
public class DynamicDataSource extends AbstractRoutingDataSource {public static final ThreadLocal < String > contextHolder = new ThreadLocal < String > ();public static final String DBTYPE_SQLITE = "sqlite";public static final String DBTYPE_DB2 = "db2";public static void setDBType(String customerType) {contextHolder.set(customerType);}public static String getDBType() {return contextHolder.get();}public static void clearDBType() {contextHolder.remove();}// 返回目标数据库@Overrideprotected DataSource determineTargetDataSource() {return super.determineTargetDataSource();}@Overrideprotected Object determineCurrentLookupKey() {return getDBType();}
}
2. ApplicationConfig 配置类关于数据源配置如下/* 数据源配置开始 */
@Bean(destroyMethod = "close")
public BasicDataSource getSqliteDataSource() {BasicDataSource sqliteDataSource = new BasicDataSource();Application appConfig = getAppConfig();sqliteDataSource.setDriverClassName(appConfig.getSqliteDriver());sqliteDataSource.setUrl(appConfig.getSqliteUrl());return sqliteDataSource;
}@
Bean(destroyMethod = "close")
public BasicDataSource getDB2DataSource() {BasicDataSource db2DataSource = new BasicDataSource();Application appConfig = getAppConfig();db2DataSource.setDriverClassName(appConfig.getDb2Driver());db2DataSource.setUrl(appConfig.getDb2Url());db2DataSource.setUsername(appConfig.getDb2User());db2DataSource.setPassword(appConfig.getDb2Password());return db2DataSource;
}// 配置动态数据源,同时添加 sqlite 和 db2 数据源
@
Bean
public DynamicDataSource getDynamicDataSource() {DynamicDataSource dynamicDataSource = new DynamicDataSource();Map < Object, Object > targetDataSources = new HashMap < Object, Object > ();BasicDataSource sqliteDataSource = getSqliteDataSource();BasicDataSource db2DataSource = getDB2DataSource();targetDataSources.put("sqlite", sqliteDataSource);targetDataSources.put("db2", db2DataSource);dynamicDataSource.setTargetDataSources(targetDataSources);dynamicDataSource.setDefaultTargetDataSource(sqliteDataSource);return dynamicDataSource;
}@
Bean
public SqlSessionFactoryBean getFactoryBean() {SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();DynamicDataSource dynamicDataSource = getDynamicDataSource();factoryBean.setDataSource(dynamicDataSource);return factoryBean;
}@
Bean
public SqlSessionTemplate getSqlSession() throws Exception {SqlSessionFactoryBean factoryBean = getFactoryBean();SqlSessionTemplate sqlSession = new SqlSessionTemplate(factoryBean.getObject());return sqlSession;
}// Spring 事务支持
@
Bean
public DataSourceTransactionManager getTransaction() {DataSourceTransactionManager transaction = new DataSourceTransactionManager();DynamicDataSource dynamicDataSource = getDynamicDataSource();transaction.setDataSource(dynamicDataSource.determineTargetDataSource());return transaction;
}
/* 数据源配置完成 */

3. 使用的时候,在具体执行的方法上添加指定数据源的代码即可

@Repository
public class TestDao implements ITestDao {@ResourceSqlSessionTemplate sqlSession;@
Overridepublic String test() {// 要连接 DB2 的话就需要添加如下这句代码DynamicDataSource.setDBType(DynamicDataSource.DBTYPE_DB2);String result = sqlSession.getMapper(ITestDao.class).test();// 清空当前线程中的数据源DynamicDataSource.clearDBType();return result;}
}

优化:第三步中的代码结构符合典型的 AOP 结构,所以,我们可以添加一个 AspectDataBase 类,控制数据源的指定和清理,并通过注解的方式让代码看起来更加优雅

  1. 首先添加一个注解类:DataBase.class,并添加如下代码
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD) // 仅作用于方法上
@Documented
public @interface DataBase {public String value() default "";
}
  1. 添加 AspectDataBase.class 做 AOP 控制,指定 DAO 层方法中需要使用的数据库
@Component
@Aspect
public class AspectDataBase {@Pointcut("execution(* com.ibm.oneteam.ah.automation.dao.*.*(..))")public void pointCut() {//nothing to do}public void methodExecuteBefore(JoinPoint jp) {//nothing to do}@Around("pointCut()")public Object methodExecuteAround(ProceedingJoinPoint pjp) throws Throwable {MethodSignature signature = (MethodSignature) pjp.getSignature();Object target = pjp.getTarget();Method method = target.getClass().getMethod(signature.getName(), signature.getParameterTypes());DataBase dataBase = method.getAnnotation(DataBase.class);if (null != dataBase) {// 设置数据源DynamicDataSource.setDBType(dataBase.value());Object result = pjp.proceed();// 清除数据源DynamicDataSource.clearDBType();return result;} else {return pjp.proceed();}}public void methodExecuteAfter(JoinPoint jp) {//nothing to do}
}

4. 在具体需要使用数据库操作的 DAO 层方法上添加 @DataBase 注解即可,如下所示

@DataBase(DynamicDataSource.DBTYPE_SQLITE)
public Admin getById(int id) {return sqliteSqlSession.getMapper(ICommonDao.class).getById(id);
}

注意

  1. 本项目使用纯代码进行配置,要使用 AOP 功能,还需要在 ApplicationConfig 类上添加 @EnableAspectJAutoProxy 注解,该注解相当于 aop:aspectj-autoproxy/(打开aop支持)

  2. 第 1 步 和第 2 步中的红色代码,如果不这样配置就会导致添加事务后数据源无法切换的问题

  3. Service 中不同数据源的方法,不能嵌套在一起,否则会抛出异常,因为两个不同的数据源交叉调用而导致事务无法正常工作;这个已经涉及到分布式事务了,可查看 springCloud 部分的笔记

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_74994.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringCloud(五)MQ消息队列

MQ概念常见消息模型helloworld案例实现实现spring AMQP发送消息实现spring AMQP接收消息工作消息队列实现发布订阅模型Fanout Exchange实现DirectExchange实现TopicExchange实现DirectExchange 和FanoutExchange的差异DirectExchange 和TopicExchange的差异基于RabbitListener注…

钉钉产品体验报告

一、调研的目的了解企业社交软件&#xff0c;借写竞品分析来帮助自己整理思路&#xff0c;看清市场的发展趋势&#xff1b;体验这类企业设计软件&#xff0c;掌握产品核心业务流程和产品结构&#xff0c;把握需求对应的功能点和界面结构&#xff0c;并侧面了解用户习惯&#xf…

用Python做数据分析有哪些优势?

众所周知&#xff0c;可以用作数据分析的语言有很多&#xff0c;包含Python、R语言等&#xff0c;而且Python被誉为数据分析的一大利器&#xff0c;更是该领域的首选语言&#xff0c;那么用Python做数据分析有哪些优势呢?跟着蛋糕往下看。 第一、Python语言自身的优势 Pytho…

ShardingSphere水平、垂直分库、分表和公共表

目录一、ShardingSphere简介二、ShardingSphere-分库分表1、垂直拆分&#xff08;1&#xff09;垂直分库&#xff08;2&#xff09;垂直分表2、水平拆分&#xff08;1&#xff09;水平分库&#xff08;2&#xff09;水平分表三、水平分库操作1、创建数据库和表2、配置分片的规则…

BigGAN

1、BIGGAN 解读1.1、作者 Andrew Brock、Jeff Donahue、Karen Simonyan 1.2、摘要 尽管最近在生成图像建模方面取得了进展&#xff0c;但从 ImageNet 等复杂数据集中 成功生成高分辨率、多样化的样本仍然是一个难以实现的目标。为此&#xff0c;我们以迄 今为止最大的规模训练生…

fastadmin:在新增页面,打开弹窗单选,参数回传

样式&#xff1a;核心代码&#xff1a;一、弹窗的控制器中&#xff1a;// 定义一个公共函数select()&#xff0c;如果这个请求是Ajax&#xff0c;则返回index()函数&#xff0c;否则返回view对象的fetch()函数。 public function select() {if ($this->request->isAjax(…

【软件测试】测试老鸟的迷途,进军高级自动化测试测试......

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 很多从业几年的选手…

【阿旭机器学习实战】【37】电影推荐系统---基于矩阵分解

【阿旭机器学习实战】系列文章主要介绍机器学习的各种算法模型及其实战案例&#xff0c;欢迎点赞&#xff0c;关注共同学习交流。 电影推荐系统 目录电影推荐系统1. 问题介绍1.1推荐系统矩阵分解方法介绍1.2 数据集&#xff1a;ml-100k2. 推荐系统实现2.1 定义矩阵分解函数2.2 …

消息中间件的概念

中间件(middleware)是基础软件的一大类&#xff0c;属于可复用的软件范畴。中间件在操作系统软件&#xff0c;网络和数据库之上&#xff0c;应用软件之下&#xff0c;总的作用是为处于自己上层的应用软件提供运行于开发的环境&#xff0c;帮助用户灵活、高效的开发和集成复杂的…

ICA简介:独立成分分析

1. 简介 您是否曾经遇到过这样一种情况&#xff1a;您试图分析一个复杂且高度相关的数据集&#xff0c;却对信息量感到不知所措&#xff1f;这就是独立成分分析 (ICA) 的用武之地。ICA 是数据分析领域的一项强大技术&#xff0c;可让您分离和识别多元数据集中的底层独立来源。 …

PPP简介,PPP分层体系架构,PPP链路建立过程及PPP的帧格式

PPP&#xff08;Point-to-Point Protocol&#xff09;是一种用于在两个网络节点之间传输数据的通信协议。它最初是为在拨号网络上进行拨号连接而开发的&#xff0c;现在已经被广泛应用于各种网络环境中&#xff0c;例如在宽带接入、虚拟专用网&#xff08;VPN&#xff09;等场景…

【JAVA】一个项目如何预先加载数据?

这里写目录标题需求实现AutowiredPostConstruct实例CommandLineRunner实例ApplicationListener实例参考需求 一般我们可能会有一些在应用启动时加载资源的需求&#xff0c;局部或者全局使用&#xff0c;让我们来看看都有哪些方式实现。 实现 Autowired 如果是某个类里需求某…

[1]MyBatis+Spring+SpringMVC+SSM整合

一、MyBatis 1、MyBatis简介 1.1、MyBatis历史 MyBatis最初是Apache的一个开源项目iBatis, 2010年6月这个项目由Apache Software Foundation迁移到了Google Code。随着开发团队转投Google Code旗下&#xff0c; iBatis3.x正式更名为MyBatis。代码于2013年11月迁移到Github。…

Vue中如何利用websocket实现实时通讯

首先我们可以先做一个简单的例子来学习一下简单的websocket模拟聊天对话的功能 原理很简单&#xff0c;有点像VUE中的EventBus&#xff0c;用emit和on传来传去 首先我们可以先去自己去用node搭建一个本地服务器 步骤如下 1.新建一个app.js&#xff0c;然后创建pagejson.js文…

【Linux】-- POSIX信号量

目录 POSIX信号量 sem_init - 初始化信号量 sem_destroy - 销毁信号量 sem_wait - 等待信号量&#xff08;P操作&#xff09; 基于环形队列的生产消费模型 数据结构 - 环形结构 实现原理 POSIX信号量 #问&#xff1a;什么是信号量&#xff1f; 1. 共享资源 -> 任何一…

【笔记】两台1200PLC进行S7 通信(1)

使用两台1200系列PLC进行S7通信&#xff08;入门&#xff09; 文章目录 目录 文章目录 前言 一、通信 1.概念 2.PLC通信 1.串口 2.网口 …

时间颗粒度选择(通过选择时间范围和颗粒度展示选项)

<template><div><el-time-selectplaceholder"起始时间"v-model"startTime":picker-options"startPickerOptions"change"changeStartTime"></el-time-select><el-time-selectplaceholder"结束时间&quo…

想招到实干派程序员?你需要这种面试法

技术招聘中最痛的点其实是不精准。技术面试官或CTO们常常会向我们吐槽&#xff1a; “我经常在想&#xff0c;能不能把我们项目中的代码打印出来&#xff0c;作为候选人的面试题的一部分&#xff1f;” “能不能把一个Bug带上环境&#xff0c;让候选人来试试怎么解决&#xf…

mysql中用逗号隔开的字段作查询用(find_in_set的使用)

mysql中用逗号隔开的字段作查询用(find_in_set的使用) 场景说明 在工作中&#xff0c;经常会遇到一对多的关系。想要在mysql中保存这种关系&#xff0c;一般有两种方式&#xff0c;一种是建立一张中间表&#xff0c;这样一条id就会存在多条记录。或者采用第二种方式&#xff…

【数据结构必会基础】关于树,你所必须知道的亿些概念

目录 1.什么是树 1.1浅显的理解树 1.2 数据结构中树的概念 2.树的各种结构概念 2.1 节点的度 2.2 根节点/叶节点/分支节点 2.3 父节点/子节点 2.4祖先节点/子孙节点 2.5兄弟节点 2.6树的度 2.7节点的层次 2.8森林 3. 如何用代码表示一棵树 3.1链式结构 3.1.1 树节…