分布式事务调用
场景描述
以请求为事务核心,一个请求内的数据库操作在一个事务
实现思路
使用批请求,请求开始启动事务,请求结束提交事务;
实现步骤
可以在前端实现也可以在后端实现
前端提供的有批请求组件和BatchRequest类,实现批相关,exec()执行批返回promise
transaction: 是否启动事务,默认true
concurrent: 是否并发执行,默认false
model/page: 页面对象(需要注意这个参数的名字,在小程序端是page,在桌面端是model)
需要注意的时,使用分布式事务时需要从市场组件中引入“批请求”组件。
一、使用组件调用
前端使用批请求组件的操作实现批请求,或者js代码中调用需要在一个批中执行的组件的操作:
使用批请求组件的操作实现批请求
多个数据集的保存在同一个批请求中执行、数据的保存和服务组件的请求需要在同一个批请求中执行或多个服务组件的请求需要在同一个批请求中执行等多种需要批请求执行的组合都可以通过批组件设置操作实现
如下:点击按钮实现两个数据集组件的保存在同一个批请求中执行
按钮的点击事件中选择操作组合,然后选择批请求组件的开始请求,最后选择批请求组件的结束请求,中间的就选择要执行的请求
js中调用需要批请求执行的组件的方法
js中调用多个数据的保存、多个服务组件的请求或者数据保存和请求同时调用等多钟组合需要在同一个批请求中执行也会可以的。
其实跟前面的代码调用是一样的,这里就只提供小程序端的一种方式的案例,另一种方式和PC端的使用,可以参考前面的使用代码调用案例
如下:小程序端实现数据保存和服务请求在同一个批请求中执行
wx.request.batch(function(batchReq){
this.comp('restData').saveAllData({batch:batchReq}); //数据组件的保存
this.comp('serviceRequest').send({batch:batchReq}); //服务组件的请求
}, {transaction: true, page: this});
二、代码调用案例
具体的使用参考下面的说明,案例中都是第三个应用中调用另外两个应用中的服务为例的,另外两个应用中的服务定义就跟不使用分布式事务一样的定义:
1 前端小程序中实现
添加引用:import BatchRequest from "$UI/wxsys/lib/base/batchRequest";
第一种调用方式:
wx.request中增加batch参数:BatchRequest对象实例,相同batch为同一个批操作,没有batch参数为立执行请求
在批中等待批提交后返回才执行success、fail、complete回调
let batchReq = new BatchRequest({page:this,transaction: true,concurrent: false});
wx.request({
batch:batchReq,
header: {
"withCredentials":true
},
url:this.getServiceUrl("/main/fenbusdemo1/fuwuy?code=2","batchdemo1"),
dataType: 'text',
method: 'POST',
success: function(res){
console.log("请求1执行成功!");
},
fail: function(err){
console.error(err);
}
});
wx.request({
batch:batchReq,
header: {
"withCredentials":true
},
url: this.getServiceUrl("/main/shiwucse/shiwucs?num=1","batchdemo2"),
dataType: 'text',
method: 'POST',
success: function(res){
console.log("请求2执行成功!");
},
fail: function(err){
console.error(err);
}
});
batchReq.exec();
第二种实现方式:
此方式简化了批请求(不需要关心批创建和提交)会自动创建BatchRequest并执行传入函数,根据函数返回(为promise时等待promise)执行BatchRequest.exec()提交批操作
var self = this;
wx.request.batch(function(batchReq){
wx.request({
batch:batchReq,
header: {
"withCredentials":true
},
url:self.getServiceUrl("/main/fenbusdemo1/fuwuy?code=5","batchdemo1"),
dataType: 'text',
method: 'POST',
success: function(res){
console.log("请求1执行成功!");
},
fail: function(err){
console.error(err);
}
});
wx.request({
batch:batchReq,
header: {
"withCredentials":true
},
url: self.getServiceUrl("/main/shiwucse/shiwucs?num=5","batchdemo2"),
dataType: 'text',
method: 'POST',
success: function(res){
console.log("请求2执行成功!");
},
fail: function(err){
console.error(err);
}
});
}, {page:this, transaction: true,concurrent: false});
2 前端桌面端UI2中实现
添加引用:var BatchRequest = require("$UI/system/lib/jquery/batchRequest");
第一种实现方式:同小程序端的第一种实现方式
var batchReq = new BatchRequest({model:this, transaction: true,concurrent: false});
$.ajax({
batch:batchReq,
xhrFields : {
withCredentials : true
},
url : this.getServiceUrl("/main/fenbusdemo1/fuwuy?code=5","batchdemo1"),
dataType : "text",
method: 'POST',
success: function(res){
console.log("请求1执行成功!");
},
fail: function(err){
console.error(err);
}
});
$.ajax({
batch:batchReq,
xhrFields : {
withCredentials : true
},
url: this.getServiceUrl("/main/shiwucse/shiwucs?num=5","batchdemo2"),
dataType: 'text',
method: 'POST',
success: function(res){
console.log("请求2执行成功!");
},
fail: function(err){
console.error(err);
}
});
batchReq.exec();
第二种实现方式:同小程序端的第二种实现方式
var self = this;
$.ajax.batch(function(batchReq){
$.ajax({
batch:batchReq,
url:self.getServiceUrl("/main/fenbusdemo1/fuwuy?code=5","batchdemo1"),
dataType: 'text',
method: 'POST',
success: function(res){
console.log("请求1执行成功!");
},
fail: function(err){
console.error(err);
}
});
$.ajax({
batch:batchReq,
url: self.getServiceUrl("/main/shiwucse/shiwucs?num=5","batchdemo2"),
dataType: 'text',
method: 'POST',
success: function(res){
console.log("请求2执行成功!");
},
fail: function(err){
console.error(err);
}
});
}, {model:this, transaction: true,concurrent: false})
3 后端服务实现
可以直接使用@GlobalTransactional注解实现,也可以使用事务函数启动分布式事务并获取分布事务ID,调用其他服务时请求头(X-DBPROXY-TRANS-INFO)传递分布事务ID,结束后提交或者回滚
String txInfo = com.justep.util.db.TransactionUtil.beginTrans();---启动事务,返回分布事务ID
com.justep.util.db.TransactionUtil.commitTrans(txInfo);---提交
com.justep.util.db.TransactionUtil.rollbackTrans(txInfo);---回滚
具体实现如下:
第一种方案:
第三个应用中的服务定义使用注解,直接通过API调用另外两个应用的服务:
需要导入相关的jar包,如下设置依赖:
组名称:com.justep 名称:tools 版本号:1.0.0
添加import com.justep.tools.db.GlobalTransactional;
具体的方法实现:
@GlobalTransactional
public void fenbusswcs() throws Exception {
//请添加你的业务代码
//调用batchdemo1应用的服务
ServiceUtil.get(SpringWebUtil.getRequest(),"batchdemo1", "/main/fenbusdemo1/fuwuy?code=5", null);
//调用batchdemo2应用的服务
ServiceUtil.post(SpringWebUtil.getRequest(),"batchdemo2","/main/shiwucse/shiwucs?num=5", null);
return;
}
第二种方案:
第三个应用中的服务定义,直接通过API调用另外两个应用的服务:
public void fenbusswcs() throws Exception {
//请添加你的业务代码
com.justep.util.db.GlobalTransaction transInfo = null;
try {
transInfo = com.justep.util.db.TransactionUtil.beginTrans();
//调用batchdemo1应用的服务
ServiceUtil.get(SpringWebUtil.getRequest(),"batchdemo1", "/main/fenbusdemo1/fuwuy?code=5", null);
//调用batchdemo2应用的服务
ServiceUtil.post(SpringWebUtil.getRequest(),"batchdemo2","/main/shiwucse/shiwucs?num=5", null);
transInfo.commit();
}catch(Exception e) {
if (transInfo != null) {
transInfo.rollback();
}
throw e;
}finally{
if (transInfo != null) {
transInfo.close();
}
}
return;
}
第三种方案:
第三个应用中的服务定义
public void fenbusswcs() throws Exception {
//请添加你的业务代码
String txInfo = com.justep.util.db.TransactionUtil.beginTrans();
String fuwu1 = batch1(txInfo);
String fuwu2 = batch2(txInfo);
if(!fuwu1.equals("OK")) {
com.justep.util.db.TransactionUtil.rollbackTrans(txInfo);
throw new Exception(fuwu1);
}
if(!fuwu2.equals("OK")) {
com.justep.util.db.TransactionUtil.rollbackTrans(txInfo);
throw new Exception(fuwu2);
}
if(fuwu1.equals("OK") && fuwu2.equals("OK")){
com.justep.util.db.TransactionUtil.commitTrans(txInfo);
}
return;
}
在第三个应用中调用另外两个应用的服务实现:
public String batch1(String txInfo) throws Exception {
String url = ServiceUtil.getServiceUrl("batchdemo1")+"/main/fenbusdemo1/fuwuy?code=5";
PostMethod method = new PostMethod(url);
method.setRequestHeader("Content-type", "application/json");
method.addRequestHeader("X-DBPROXY-TRANS-INFO", txInfo);
method.releaseConnection();
HttpClient client = new HttpClient();
client.executeMethod(method);
String responses = method.getResponseBodyAsString();
if(method.getStatusCode()==200) {
return "OK";
}else
return responses;
}
public String batch2(String txInfo) throws Exception{
String url = ServiceUtil.getServiceUrl("batchdemo2")+"/main/shiwucse/shiwucs?num=5";
PostMethod method = new PostMethod(url);
method.setRequestHeader("Content-type", "application/json");
method.addRequestHeader("X-DBPROXY-TRANS-INFO", txInfo);
method.releaseConnection();
HttpClient client = new HttpClient();
client.executeMethod(method);
String responses = method.getResponseBodyAsString();
if(method.getStatusCode()==200) {
return "OK";
}else
return responses;
}
案例说明:
batchdemo1、batchdemo2是另外两个应用的应用名
/main/fenbusdemo1/fuwuy、/main/shiwucse/shiwucs是服务地址
code和num是服务的参数
前端在调用请求的时候url的拼接方式需要参考上面案例中的方式使用API拼接