SpringBoot项目多数据源配置与MyBatis拦截器生效问题解析

news2024/11/16 3:34:43

在日常项目开发中,由于某些原因,一个服务的数据源可能来自不同的库,比如:

  1. 对接提供的中间库,需要查询需要的数据
  2. 同步数据,需要将一个库的数据同步到另一个库,做为同步工具的服务
  3. 对接第三方系统,由于时间等原因不方便提供接口,开放数据库提供一些查询视图

在实际项目中,多数据源的配置是常见需求。本博客将详细介绍如何在Spring Boot项目中配置多个数据源,并使用MyBatis进行整合。同时,我们将解决在多数据源配置下自定义拦截器不生效的问题。

1、配置多数据源方式

我所在项目使用的SpringBoot+Mybatis,首先需要添加不同数据源的配置,一般为了区分数据源,会是在单数据源的基础上再datasource节点和具体配置节点之间自定义数据源的名称,比如default表示默认数据源。

spring:
  thymeleaf:
    cache: false
  datasource:
  # 默认数据源配置
    default:
      name: dataSource
      url: jdbc:log4jdbc:Postgresql://ip:port/db?currentSchema=db&ApplicationName=test
      username: ENCRYPT#wwWQEj+qg=
      password: ENCRYPT#Jb0N1GHFwD+MWQRiSfHg==
      driver-class-name: net.sf.log4jdbc.sql.jdbcapi.DriverSpy
      type: com.alibaba.druid.pool.DruidDataSource
      druid:
        validation-query: select 1
        min-idle: 20
        initial-size: 20
        max-active: 50
   # 第二数据源
    secondary:
      name: dataSource
      url: jdbc:log4jdbc:Postgresql://ip:port/db?currentSchema=db&ApplicationName=test
      username: ENCRYPT#qdTt8x5ss=
      password: ENCRYPT#YvZIPJii8D+MWQRiSfHg==
      driver-class-name: net.sf.log4jdbc.sql.jdbcapi.DriverSpy
      type: com.alibaba.druid.pool.DruidDataSource
      druid:
        validation-query: select 1
        min-idle: 20
        initial-size: 20
        max-active: 50

因为有了多个数据源,需要手动配置创建数据源实例, 默认数据源添加@Primary注解进行标注。

@Configuration
public class DataSourceConfig {
​
    /**
     * 构建主数据源
     * @return 数据源对象
     */
    @Primary
    @Bean(name = "defaultDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.default")
    public DataSource defaultDataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        return dataSource;
    }
​
    /**
     * 第二数据源
     * @return 数据源对象
     */
    @Bean(name = "secondaryDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.secondary")
    public DataSource secondaryDataSource() {
        return new DruidDataSource();
    }
}

2、Mybatis配置

使用单数据源时,Mapper文件的扫描一般是在启动类上使用@MapperScan配置扫描路径的,但是多数据源则需要进行区分,扫描不同的路径;

SqlSessionFactory 在 MyBatis 中充当着重要的角色,它通过将配置信息加载到内存中,创建和管理 SqlSession 实例,以及配置和创建 Mapper 对象,为开发者提供了便捷的数据库操作接口。通常情况下,一个应用程序只需要创建一个 SqlSessionFactory 实例,并在整个应用程序的生命周期中共享该实例。多数据源场景下,不同的数据源也需要创建自己的SqlSessionFactory 实例。

@MapperScan(basePackages = {"com.xx.xx.*.mapper", "com.xx.xx.**.mapper",
    "com.xx.xx.report.provider.report.db"},
    sqlSessionFactoryRef = "defaultSqlSessionFactory", sqlSessionTemplateRef = "defaultSqlSessionTemplate")
@Configuration
public class DefaultMybatisConfig {
​
    @Autowired
    @Qualifier("dataSource")
    private DataSource defaultDataSource;
​
    @Resource
    private DictGroupTagInterceptor dictGroupTagInterceptor;
​
    @Resource
    private PageInterceptor pageInterceptor;
​
    @Resource
    private DefaultMybatisInterceptor defaultMyBatisInterceptor;
​
    @Bean(name = "defaultTransactionManager")
    @Primary
    public DataSourceTransactionManager defaultTransactionManager() {
        return new DataSourceTransactionManager(defaultDataSource);
    }
​
    @Primary
    @Bean(name = "defaultSqlSessionFactory")
    public SqlSessionFactory defaultSqlSessionFactory() throws Exception {
        final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(defaultDataSource);
        // Artery的分页插件,数据字典插件(用于动态替换模式名称)
        sessionFactory.setPlugins(pageInterceptor, dictGroupTagInterceptor, defaultMyBatisInterceptor);
        return sessionFactory.getObject();
    }
​
​
    @Bean(name = "defaultSqlSessionTemplate")
    public SqlSessionTemplate defaultSqlSessionTemplate(
        @Qualifier("defaultSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

对于其他数据源的Mybatis配置也是一样,创建配置类进行配置即可

3、过程中遇到的问题

服务正常启动,对应数据源的访问也没有问题;一个业务场景需要用到业务表的创建时间,创建时间为null导致系统异常。

排查发现,业务表最近的业务数据的创建人,创建时间,修改人,修改时间字段值都是空的。

这些基础字段并不是通过业务数据的创建和修改赋值的,都是实现Mybatis的拦截器Interceptor进行实现的,说明拦截器并未生效。

判断当前操作是新增或者修改,根据当前登录人信息对基础字段赋值

@Override
public Object intercept(Invocation invocation) throws Throwable {
​
    Object[] args = invocation.getArgs();
    IUser currentUser = securityService.getCurrUserInfo();
    if(Objects.isNull(currentUser)){
        log.warn("没有登录的用户在操作数据库,请关注,参数:{},线程:{}",args,Thread.currentThread().getName());
        currentUser = new User();
        currentUser.setCorpId("unkown");
        currentUser.setId("unkown");
    }
    if (args.length > 1) {
        MappedStatement ms = (MappedStatement)args[0];
        try {
            if (SqlCommandType.INSERT.equals(ms.getSqlCommandType())) {
                preInsert(args[1], currentUser);
            } else if (SqlCommandType.UPDATE.equals(ms.getSqlCommandType())) {
                preUpdate(args[1], currentUser);
            }
        } catch (Exception e) {
            log.warn("intercept exception : " + e.getMessage(), e);
        }
    }
​
    return invocation.proceed();
}

1、那么系统单数据源时,自定义的拦截器是如何生效的呢?

原因就在于MybatisAutoConfiguration类 ,作用是自动配置 MyBatis 相关的组件,包括创建SqlSessionFactoryMybatisAutoConfiguration实现了InitializingBean进行初始化操作,在实例化时注入了Interceptor对象。

通过跟踪代码发现,SqlSessionFactory会在MybatisAutoConfiguration创建中创建实例,而这些自定义的拦截器会在创建SqlSessionFactory实例的时候进行设置。

其中注解@ConditionalOnMissingBean表示容器中不存在某个指定类型或名称的 Bean 时,才会生效。所以在SqlSessionFactory已经存在的实例情况之下并不会创建实例,也就解释了我们配置多数据源情况下自定义拦截器不生效的原因。

所以解决办法就很简单了,在我们自定义的Mybatis配置类中注入自定义的拦截器,设置到SqlSessionFactory当中;

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

黑马Java——面向对象进阶(static继承)

1.static静态变量 静态变量是随着类的加载而加载的,优先与对象出现的

“豚门”、“吗喽”,为啥品牌宣传瞄上网红动物?

近期,新茶饮品牌喜茶联名红山动物园,凭借可爱周边拿捏无数消费者,再往前一段时间,还有奈雪联名“吗喽”表情包,为什么品牌宣传会瞄上网红动物,今天媒介盒子就来和大家聊聊。 一、 萌元素引起用户情绪共鸣 …

C#使用DateTime.Now.AddDays方法获取任一天的信息

目录 一、使用DateTime对象的AddDays方法获取任一天信息方法 二、举例说明获取昨天的信息 三、涉及到的知识点 1. MessageBox.Show()中信息分行的办法 使用DateTime.Now属性可以得到当前的日期信息,此时调用ToString方法,并在该方法中添加…

使用PHP自定义一个加密算法,实现编码配合加密,将自己姓名的明文加密一下

<meta charset"UTF-8"> <?phpfunction customEncrypt($lin, $key mySecretKey){// 定义一个简单的替换规则$li array(L > M, I > Y, Y > O, A > N, E > Q, );$yan ;for($i 0; $i < strlen($lin); $i){$char $lin[$i];if(isset($li[…

27.移除元素(力扣LeetCode)

文章目录 27.移除元素&#xff08;力扣LeetCode&#xff09;题目描述方法一&#xff1a;vector成员函数&#xff1a;erase方法二&#xff1a;暴力解法方法三&#xff1a;双指针法 27.移除元素&#xff08;力扣LeetCode&#xff09; 题目描述 给你一个数组 nums 和一个值 val&…

6.php开发-个人博客项目Tp框架路由访问安全写法历史漏洞

目录 知识点 php框架——TP URL访问 Index.php-放在控制器目录下 ​编辑 Test.php--要继承一下 带参数的—————— 加入数据库代码 --不过滤 --自己写过滤 --手册&#xff08;官方&#xff09;的过滤 用TP框架找漏洞&#xff1a; 如何判断网站是thinkphp&#x…

最小二乘2D圆拟合(高斯牛顿法)

欢迎关注更多精彩 关注我&#xff0c;学习常用算法与数据结构&#xff0c;一题多解&#xff0c;降维打击。 本期话题&#xff1a;最小二乘2D圆拟合 相关背景资料 点击前往 2D圆拟合输入和输出要求 输入 8到50个点&#xff0c;全部采样自圆上&#xff0c;z轴坐标都为0。每个…

算法练习-螺旋矩阵(思路+流程图+代码)

难度参考 难度&#xff1a;中等 分类&#xff1a;数组 难度与分类由我所参与的培训课程提供&#xff0c;但需要注意的是&#xff0c;难度与分类仅供参考。以下内容均为个人笔记&#xff0c;旨在督促自己认真学习。 题目 给定一个正整数n&#xff0c;生成一个包含1到 n^2 所有元…

基于Vue+Canvas实现的画板绘画以及保存功能,解决保存没有背景问题

基于VueCanvas实现的画板绘画以及保存功能 本文内容设计到的画板的js部分内容来源于灵感来源引用地址&#xff0c;然后我在此基础上&#xff0c;根据自己的需求做了修改&#xff0c;增加了其他功能。 下面展示了完整的前后端代码 这里写目录标题 基于VueCanvas实现的画板绘…

面向对象、封装、继承、多态、JavaBean

二、面向对象 什么是对象 什么是对象&#xff1f;之前我们讲过&#xff0c;对象就是计算机中的虚拟物体。例如 System.out&#xff0c;System.in 等等。然而&#xff0c;要开发自己的应用程序&#xff0c;只有这些现成的对象还远远不够。需要我们自己来创建新的对象。 1. 抽…

confluence模版注入漏洞_CVE-2023-22527

1. 漏洞简介 Confluence是Atlassian公司开发的一款专业的企业知识管理与协同软件&#xff0c;可用于构建企业wiki。 Confluence Data Center和Confluence Server多个受影响版本中存在模板注入漏洞&#xff0c;未经身份验证的威胁者可利用该漏洞在受影响的实例上实现远程代码执…

暗藏危险,警惕钓鱼邮件!

叮 您有一份福利待查收 您的信息资产需要排查 您的账户异常需要验证 这些看似“重要”的邮件 都藏着攻击者的恶意嘴脸 随着网络安全防护和建设的重要性日益凸显&#xff0c;国家安全、企业安全、合规需求及业务驱动等各个方面都亟需将网络安全作为基石。在企业业务转型发展…

v-on、事件修饰符、v-model、一些常用指令

v-on 事件修饰符 Vue.js 为 v-on 提供了事件修饰符来处理 DOM 事件细节&#xff0c;如&#xff1a;event.preventDefault() 或 event.stopPropagation()。 Vue.js 通过由点 . 表示的指令后缀来调用修饰符。 .stop - 阻止冒泡 .prevent - 阻止默认事件 .capture - 阻止捕获 .s…

【jetson笔记】解决vscode远程调试qt.qpa.xcb: could not connect to display报错

配置x11转发 jetson远程安装x11转发 安装Xming Xming下载 安装完成后打开安装目录C:\Program Files (x86)\Xming 用记事本打开X0.hosts文件&#xff0c;添加jetson IP地址 后续IP改变需要重新修改配置文件 localhost 192.168.107.57打开Xlaunch Win菜单搜索Xlaundch打开 一…

递归和尾递归(用C语言解斐波那契和阶乘问题)

很多人都对递归有了解&#xff0c;但是为尾递归很少&#xff0c;所以这次来专门讲一讲关于尾递归的一些问题。 什么是尾递归 如果一个函数中所有递归形式的调用都出现在函数的末尾&#xff0c;我们称这个递归函数是尾递归的。因为在一些题目的做法中&#xff0c;我们可以发现…

JavaFX场景入门

目录 JAVAFX jdk1.8以上引入javafx类库 JDK11JAVAFX(eclipse) 小知识点 舞台Stage platform、screen类 Scene场景类 查看电脑屏幕宽高 Group容器 JAVAFX项目 Image javafx场景 javaFx文本 javaFX颜色 JAVAFX jdk1.8以上引入javafx类库 JDK11JAVAFX(eclipse) 方式…

MySQL分组,获取组内最新的10条数据

一、记录 记录一次SQL&#xff0c;最近在项目中遇到了一个相对比较复杂的SQL。 要求依据分组&#xff0c;获取每个分组后的前10条数据。 分组查询最新的数据&#xff0c;应该都做过&#xff0c;但是获取前10条数据&#xff0c;还是没处理过的。 二、处理 2.1 前期数据准备 …

数据分析-Pandas如何用图把数据展示出来

数据分析-Pandas如何用图把数据展示出来 俗话说&#xff0c;一图胜千语&#xff0c;对人类而言一串数据很难立即洞察出什么&#xff0c;但如果展示图就能一眼看出来门道。数据整理后&#xff0c;如何画图&#xff0c;画出好的图在数据分析中成为关键的一环。 数据表&#xff…

【进入游戏行业选游戏特效还是技术美术?】

进入游戏行业选游戏特效还是技术美术&#xff1f; 游戏行业正处于蓬勃发展的黄金时期&#xff0c;科技的进步推动了游戏技术和视觉艺术的飞速革新。在这个创意和技术挑战交织的领域里&#xff0c;游戏特效和技术美术岗位成为了许多人追求的职业目标。 这两个岗位虽然紧密关联…

5.ROC-AUC机器学习模型性能的常用的评估指标

最近回顾机器学习基础知识部分的时候&#xff0c;看到了用于评估机器学习模型性能的ROC曲线。再次记录一下&#xff0c;想起之前学习的时候的茫然&#xff0c;希望这次可以更加清晰的了解这一指标。上课的时候听老师提起过&#xff0c;当时没有认真去看&#xff0c;所以这次可以…