一个项目中使用多个数据源的需求,我们在日常工作中时常会遇到。
以商城系统为例,有一个 MySQL 的数据库负责存储交易数据。公司还有一套 ERP 企业信息化管理系统,要求订单信息同步录入 ERP 数据库,便于公司统一管理,而该 ERP 系统采用的数据库为 SQL Server 。
此时,就可以在 Spring Boot 项目中配置多个数据源。另外,使用多数据源后,需要采用分布式事务来保持数据的完整性。
本小节我们使用 Spring Boot 开发一个商城系统的订单生成功能,订单信息同时进入 MySQL 与 SQL Server 数据库。
首先创建 MySQL 数据库 shop ,并新建订单表 order ,表结构如下:
然后创建 SQL Server 数据库 erpshop ,并新建订单表 erp_order ,表结构如下。注意 id 是自增长的唯一标识,out_id 是对应订单在 MySQL 数据库中的唯一标识,以便在两个库中比对订单。
接下来,我们开始实现 Spring Boot 后端项目,数据持久层采用 MyBatis 框架,同时访问两个数据源。
我们引入热部署依赖、 Web 依赖、数据库访问相关依赖及测试相关依赖,具体如下:
由于我们要同时访问两个数据库,所以需要在配置文件中添加两个数据源的配置信息。注意配置多数据源时, url 配置需要使用 spring.datasource.db1.jdbc-url=xxx
的形式。
多个数据源的情况下, 我们需要通过配置类,将数据源注册为组件放入 Spring 容器中。
classpath:mapper1/
目录去寻找。这样数据源 – DAO 数据访问接口 – 映射文件三者的对应关系就建立起来了。
这两个接口中使用的数据对象比较简单,代码如下:
数据操作接口与对应的映射文件均已编写完毕,现在可以通过测试类进行多数据源测试了,我们在测试类中同时向两个库插入记录。
运行测试方法后,两个数据库表中均新增数据成功,这样我们就成功的使用 Spring Boot 同时操作了两个数据源。
采用多数据源之后,事务的实现方式也随之发生变化。当某个数据源操作出现异常时,该数据源和其他数据源的事务都需要回滚。这种涉及多个数据源的事务,称为分布式事务,接来下我们就来具体实现一下。
在 pom.xml 引入 Atomikos 事务管理器相关的依赖项, Atomikos 是一个开源的事务管理器,支持分布式事务。
继续修改 DataSourceConfig 类,在其中配置分布式事务管理器组件。当项目中使用事务时,会通过配置的分布式事务管理器管理分布式事务操作。
在测试方法上添加 @Transactional
开启事务,然后在两个数据源操作中间模拟抛出异常。
此时运行测试类,可以发现数据源 1 的事务已回滚,验证成功!
在开发 Spring Boot 项目时,如果默认配置满足不了我们的需求,可以通过手工配置组件实现我们需要的功能。这些组件可能是各个公司提供的,我们根据相应文档,为其配置各个属性即可。
关于Spring的事务,它是Spring Framework
中极其重要的一块。前面用了大量的篇幅从应用层面、原理层面进行了比较全方位的一个讲解。但是因为它过于重要,所以本文继续做补充内容:Spring事务的同步机制
(后面还有Spring事务的监听机制)
Spring事务同步机制?
我估摸很多小伙伴从来没听过还有这么一说法,毕竟它在平时开发中你可能很少遇到(如果你没怎么考虑过系统性能和吞吐量的话)。
让我记录本文的源动力是忆起两年前自己在开发、调试过程中遇到这样一个诡异异常:
预期结果:本以为第二个insert是插入不进去的(不是报错,而是持久化不了),但是最终结果是:两条记录都插入成功了。
what a fuck,有点打我脸,挺疼。
,与我之前掌握的理论相悖了,与Spring的javadoc里讲述的也相悖了(其实与Spring的并没有相悖,毕竟人家说的是“可能”,可见话不能说太满的重要性,哈哈)。这勾起了我的深入探索,究竟咋回事呢???
下面我把我的研究结果直接描述如下:
connection
的自动提交功能可能是在这期间被恢复了,从而导致了这条SQL语句它的自动提交成功。
关于
Connection
的自动提交机制,以及事务对它的“影响干预”,请参与上面的推荐博文了解,有详细的表述
// 这里最终都会被执行~~~
// 子类可以根据自己的需要,自己去实现事务提交完成后的操作
// 这里是关键,在事后会恢复链接的自动提交本能,也就是常用的恢复现场机制嘛~~
这个内部类很简单,就是聚合了一些属性值,此处我们只关注
mustRestoreAutoCommit这个属性值是否被设置为true
了,若被设置过,就符合我的预期和猜想了。
此处代码也就是当开启事务(doBegin
)的时候的关键代码,它对DataSourceTransactionObject
打入标记,表示最终需要事务它返还给链接自动提交的能力。
综上所述:上述案例Demo最终成功插入了两条数据的结果是完全正确,且我的猜想都解释通了。
备注:case2我本想构造的是在
afterCommit()
里使用connection而最终被错误关闭的情况case,目前来看若使用的是DataSourceTransactionManager
这个事务管理器的话,是不用担心这种情况发生的,最终你的SQL都会被成功提交,也不会出现被误close掉的问题~
这一篇文章的主旨是讲解Spring事务的同步机制,虽然这以能力可能是Spring提供的小众功能,但正所谓小脾气、大能力描述它就很贴切~
我认为如果你真的想去了解一门技术的时候,还是不要放过每一个细节,把它融汇贯通,这样再学习一个新的技术就很容易举一反三了。(因为没有一句代码、注释都是无用的,否则它是废代码,就没有存在的必要)
最后可以分享一个找问题的小小小技巧:大胆猜测,小心求证
版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。