magic-api 是一个基于Java的接口快速开发框架,编写接口将通过magic-api提供的UI界面完成,自动映射为HTTP接口,无需定义Controller、Service、Dao、Mapper、XML、VO等Java对象即可完成常见的HTTP API接口开发。解决了应用部署 编译-打包-部署等一系列上线操作流程;实现接口的快速开发上线等等。同时它还支持多数据源下的数据操作,解决了各个数据库之间的数据交互问题。但是当前版本下并未实现跨数据源分布式事务,在多数据源数据写入时有可能会造成“脏数据”。当前市面上有很多实现分布式事务的产品,最终选择seata来扩展magic的事务功能。
由于我是一个springcloud项目,从seata官方案例中我们知道想要集成seata需要将DataSource外面再包一层DataSourceProxy
首先我们拉去magic-api的源码
现在我们要找到magic-api(我用的是1.7.5)的DataSource注册方法org.ssssssss.magicapi.provider.impl.DefaultMagicAPIService下的createDataSource方法
private DataSource createDataSource(DataSourceInfo properties) {
Class<? extends DataSource> dataSourceType = getDataSourceType(properties.get("type"));
if (!properties.containsKey("driverClassName")
&& properties.containsKey("url")) {
String url = properties.get("url");
String driverClass = DatabaseDriver.fromJdbcUrl(url).getDriverClassName();
properties.put("driverClassName", driverClass);
}
DataSource dataSource = BeanUtils.instantiateClass(dataSourceType);
ConfigurationPropertySource source = new MapConfigurationPropertySource(properties);
ConfigurationPropertyNameAliases aliases = new ConfigurationPropertyNameAliases();
aliases.addAliases("url", "jdbc-url");
aliases.addAliases("username", "user");
Binder binder = new Binder(source.withAliases(aliases));
binder.bind(ConfigurationPropertyName.EMPTY, Bindable.ofInstance(dataSource));
//这里在DataSource 包一层DataSourceProxy 然后返回代理后的DataSource
DataSourceProxy dataSourceProxy = new DataSourceProxy(dataSource);
return dataSourceProxy;
}
注意:如果缺包的话记得maven引入seata的包
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-seata</artifactId>
<version>2.1.0.RELEASE</version>
<exclusions>
<exclusion>
<artifactId>seata-all</artifactId>
<groupId>io.seata</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
<version>1.3.0</version>
</dependency>
然后在org.ssssssss.magicapi.modules.SQLModule中新增一个方法来获取全局事务对象GlobalTransaction
@Comment("开启分布式事务,返回事务对象")
public GlobalTransaction globalTransaction() {
GlobalTransaction tx = GlobalTransactionContext.getCurrentOrCreate();
return tx;
}
然后将整个magic-api项目编译打包并将magic-api-1.7.5.jar加载到maven本地库,加载脚本如下。
mvn install:install-file -Dfile=D:\programs\java\magic-api\magic-api\target\magic-api-1.7.5.jar -DgroupId=org.ssssssss -DartifactId=magic-api -Dversion=1.7.5 -Dpackaging=jar
在项目中引入seata的配置重载项目将新magic-api与seata加载到项目中。这样就集成完成了,我们按照seata官方的指引在项目中引入file.conf和registry.conf启动seata-server,启动项目就可以安心进行跨库操作了。
测试一下(下面是在magic-api中写api脚本):
//获取全局事务
var tx = db.globalTransaction();
try {
//启动全局事务
tx.begin(10000, "test-client");
var sql = """insert into t_test(test) values ('aa')"""
//不同数据源写入操作
db.ry_cloud.update(sql)
db.inside_db.update(sql)
throw '来个错误' //测试下报错
} catch (e) {
//报错回滚
tx.rollback();
log.info("报错啦!错误信息:{}", e.getMessage())
exit 400, e.getMessage()
}
//正常提交
tx.commit();
运行结果:
03/02 14:40 INFO o.s.m.c.MappingHandlerMapping : 刷新接口:测试,POST:/system_api/third_api/test
03/02 14:40 INFO r.b.i.CustomRequestInterceptor: 用户:[null],请求接口:[测试],请求参数:null,请求体:null
03/02 14:40 INFO s.t.a.DefaultGlobalTransaction: Begin new global transaction [10.51.64.62:8091:2004416281]
要执行的SQL:insert into t_test(test) values ('aa')
要执行的SQL参数:[]
03/02 14:40 INFO 测试(/system_api/third_api/test): 执行SQL:insert into t_test(test) values ('aa')
03/02 14:40 INFO 测试(/system_api/third_api/test): 数据源:主数据源
要执行的SQL:insert into t_test(test) values ('aa')
要执行的SQL参数:[]
03/02 14:40 INFO 测试(/system_api/third_api/test): 执行SQL:insert into t_test(test) values ('aa')
03/02 14:40 INFO 测试(/system_api/third_api/test): 数据源:内网数据库
03/02 14:40 INFO .p.c.RmBranchRollbackProcessor: rm handle branch rollback process:xid=10.51.64.62:8091:2004416281,branchId=2004416285,branchType=AT,resourceId=jdbc:mysql://mysql.com:3306/data_trans,applicationData=null
03/02 14:40 INFO io.seata.rm.AbstractRMHandler : Branch Rollbacking: 10.51.64.62:8091:2004416281 2004416285 jdbc:mysql://mysql.com:3306/data_trans
03/02 14:40 INFO s.r.d.u.AbstractUndoLogManager: xid 10.51.64.62:8091:2004416281 branch 2004416285, undo_log deleted with GlobalFinished
03/02 14:40 INFO io.seata.rm.AbstractRMHandler : Branch Rollbacked result: PhaseTwo_Rollbacked
03/02 14:40 INFO .p.c.RmBranchRollbackProcessor: rm handle branch rollback process:xid=10.51.64.62:8091:2004416281,branchId=2004416283,branchType=AT,resourceId=jdbc:mysql://mysql.com:3306/ry_cloud,applicationData=null
03/02 14:40 INFO io.seata.rm.AbstractRMHandler : Branch Rollbacking: 10.51.64.62:8091:2004416281 2004416283 jdbc:mysql://mysql.com:3306/ry_cloud
03/02 14:40 INFO s.r.d.u.AbstractUndoLogManager: xid 10.51.64.62:8091:2004416281 branch 2004416283, undo_log deleted with GlobalFinished
03/02 14:40 INFO io.seata.rm.AbstractRMHandler : Branch Rollbacked result: PhaseTwo_Rollbacked
03/02 14:40 INFO s.t.a.DefaultGlobalTransaction: [10.51.64.62:8091:2004416281] rollback status: Rollbacked
03/02 14:40 INFO 测试(/system_api/third_api/test): 报错啦!错误信息:来个错误
查看两个数据库中的t_test表 数据未写入。去掉事务代码虽然报错了但是两个库中的表数据都插入成功。分布式事务集成成功。