数据集事件
数据集提供了查询数据、插入/更新数据和删除数据的方法,同时提供了执行这些方法的前后事件。用于查询、插入/更新和删除数据时,实现一些扩展需求。
例如下面这几个场景:
- 在采购表中插入一条采购数据时,要同时更新库存表中的数据
- 在更新用户表的用户姓名后,更新其他表里面存储的用户姓名
- 在删除一个商品分类后,将商品表中的商品分类清空
- 在《数据集参数》一节中讲到的,使用输出参数对手机号进行脱敏的能力,可以使用本节介绍的方法实现,在查询数据后,对数据进行处理,最后将处理后的数据返回前端
也适用于以下几个场景:
- 插入/更新A表信息前,进行逻辑判断是否可以操作,如果不可以操作更新失败并且失败内容提醒用户
- 插入/更新A表之后需要更新B表,操作失败进行事务回滚
- 插入/更新A表前,需要查询其他补充A表的其他字段内容,再进行插入A表
系统提供的数据集事件,是在代码层使用 Spring Event 事件发布/监听机制实现的。并且数据集事件和方法在同一个事务内。
下面以调用数据集的插入/更新数据方法为例,说明执行方法和事件的执行过程。
- 在前端发送插入/更新数据的请求
- 系统派发一个事件,事件中的参数有服务名、模块名、数据集标识、数据表名、状态(执行开始)、插入/更新的数据等信息,在事件中设置是否已经处理完毕(默认 false)
- 未处理完,系统执行插入/更新数据方法
- 如果插入/更新成功,系统派发一个事件,事件中的参数有服务名、模块名、数据集标识、数据表名、状态(执行成功)等信息
- 如果插入/更新失败,系统派发一个事件,事件中的参数有服务名、模块名、数据集标识、数据表名、状态(执行失败)等信息
- 已处理完,不再执行插入/更新数据方法,这种方式也称为全接管,即只执行开发者的代码,不执行系统的方法
- 未处理完,系统执行插入/更新数据方法
添加数据集事件
增加依赖设置
引入系统工具类
组名称 | 名称 | 版本号 |
---|---|---|
com.justep | tools | 1.0.0 |
增加数据集事件处理类
在 service 目录下,新增 java 文件,例如:EventHandler.java。
增加 @Component 注解
使用 @Component 方式实现 Spring Event,在 class 前面添加 @Component 注解,代码如下:
@Component
public class EventHandler {
}
使用 condition 进行过滤
condition 中的条件使用 event 属性任意组合,支持并且和或者。在 java 方法前面,添加 @EventListener(condition = "#event.属性 == 值"),用来区分是谁发起的事件,代码如下:
@EventListener(condition = "#event.moduleName == 'main' && #event.dataModelName == 'buy' && #event.method == 'upsert'")
public void upsertStore(DbUpsertEvent event) throws Exception {
}
表示来自 main 模块、buy 数据集的插入/更新方法会触发本事件。
判断事件的状态
在执行动作时,事件会触发两次,第一次的 status 为 START,第二次的 status 为 SUCCEED 或 FAILED,状态为枚举类型:
public enum Status {
START, SUCCEED, FAILED
}
使用下面的代码判断事件的状态是否为 START:
if(event.getStatus()==Status.START)
插入/更新的事件
事件参数
插入/更新事件的参数为 DbUpsertEvent 类,构造方法如下:
public DbUpsertEvent(JSONObject request, JSONArray data, Connection conn, String service, String tableName, String dataModelName, List<String> conditionFields) {
super(METHOD_UPSERT, request, conn, service, tableName, dataModelName, conditionFields, null, null);
this.data = data;
}
部分接管案例
案例:插入/更新采购表,更新库存表
采购表 buy,包括主键 fid,用品 goods,数量 num 列 库存表 store,包括主键 fid,用品 goods,库存数 num 列 采购表的 goods 和库存表的 goods 对应,插入/更新采购表,则使用 goods 更新库存表 代码如下:
@Autowired
BuyMapperPlus mapperBuy;
@Autowired
StoreMapperPlus mapperStore;
@EventListener(condition="#event.tableName=='main_buy' && #event.method=='upsert' && #event.moduleName=='main'")
public void upsertStore(DbUpsertEvent event) throws Exception {
if(event.getStatus()!=Status.START) return;
//获取服务名
String serviceName = ContextUtil.getServiceName();
//获取插入/更新的数据
JSONArray data = event.getData();
for(int i=0;i<data.size();i++) {
JSONObject row = data.getJSONObject(i);
String fid = row.getString("fid");
String goods = row.getString("goods");
Integer num = row.getInteger("num");
//使用《2.3.2 java SDK》一节中介绍的系统提供的方法查询库存表
DbrestWrapper<?> wrapperStore = (DbrestWrapper<?>)new DbrestWrapper<String>(serviceName, "main", "store");
wrapperStore.eq("goods",goods);
List<Store> listStore = DbrestUtil.selectList(wrapperStore, Store.class);
//也可以使用mybatisPlus的方法查询采购表
QueryWrapper<Buy> wrapperBuy = new QueryWrapper<>();
wrapperBuy.eq("fid",fid);
List<Buy> listBuy = mapperBuy.selectList(wrapperBuy);
//计算更新前的数量
Integer oldNum = 0;
if(listBuy.size()>0) {
oldNum = listBuy.get(0).getNum();
if(oldNum == null) {
oldNum = 0;
}
}
//更新库存表,也可以使用mybatis的方法更新
Store store = new Store();
if(listStore.size()==0) {
store.setFid(RandomUtil.uuid());
store.setGoods(goods);
store.setNum(num - oldNum);
store.setVersion(0);
}else {
Store s = listStore.get(0);
store.setFid(s.getFid());
store.setNum(s.getNum() + num - oldNum);
store.setVersion(s.getVersion() + 1);
}
List<Store> storeList = new ArrayList<Store>();
storeList.add(store);
DbrestResult ret = DbrestUtil.upsert(serviceName, "main", "store", storeList);
}
}
全接管案例
同时更新多张表(全接管,返回 handled=true)
客户表 customer,包括主键 fid、客户名 name,销售员ID salerID 列 销售员表 saler,包括主键 fid、姓名 name、手机号 phone 列 添加一个 sql 数据集 customerSaler,关联查询客户表和销售员表。sql 数据集可以更新到一张表,通过事件更新另一张表,也可以不使用 sql 数据集的更新到一张表,通过事件更新两张表。本例采用后者,代码如下:
@EventListener(condition="#event.dataModelName=='customerSaler' && #event.method=='upsert' && #event.moduleName=='main'")
public void upsertMutilTable(DbUpsertEvent event) throws Exception {
if(event.getStatus()!=Status.START) return;
//获取服务名
String serviceName = ContextUtil.getServiceName();
//获取插入/更新的数据
JSONArray data = event.getData();
List<Saler> salerList = new ArrayList<Saler>();
for(int i=0;i<data.size();i++) {
//获得销售员数据
JSONObject row = data.getJSONObject(i);
String salerId = row.getString("salerID");
String salerName = row.getString("saler_name");
String salerPhone = row.getString("saler_phone");
//获取销售员表的数据
DbrestWrapper<?> wrapperSaler = (DbrestWrapper<?>)new DbrestWrapper<String>(serviceName, "main", "saler");
wrapperSaler.eq("fid",salerId);
List<Saler> listSaler = DbrestUtil.selectList(wrapperSaler, Saler.class);
//修改销售员表的数据
if(listSaler.size()>0) {
Saler saler = new Saler();
Saler s = listSaler.get(0);
saler.setFid(s.getFid());
saler.setName(salerName);
saler.setPhone(salerPhone);
salerList.add(saler);
}
}
//更新销售员表
DbrestResult ret = DbrestUtil.upsert(serviceName, "main", "saler", salerList);
//插入/更新客户表
List<UpsertWrappers<Customer>> list = DbrestUtil.createUpsertWrappers(event, Customer.class);
int i=0;
for (UpsertWrappers<Customer> wrappers: list) {
List<Customer> obj = mapperCustomer.selectList(wrappers.getQueryWrapper());
if (obj != null && obj.size() > 0) {
i += mapperCustomer.update(wrappers.getEntity(), wrappers.getQueryWrapper());
} else {
mapperCustomer.insert(wrappers.getEntity());
}
}
//准备返回数据
JSONObject json = new JSONObject();
json.put("status", 200);
json.put("result", "total:" + list.size() + ", updated:" + i);
json.put("eventHandled", true);
event.setResult(json);//设置返回数据
event.setHandled(true);//设置全接管
}
删除数据的事件
事件参数
删除事件的参数为 DbDeleteEvent 类,构造方法如下:
public DbDeleteEvent(JSONObject request, JSONArray data, Connection conn, String service, String tableName, String dataModelName, List<String> conditionFields, List<String> conditionOperations, List<Object> paramValues) {
super(METHOD_DELETE, request, conn, service, tableName, dataModelName, conditionFields, conditionOperations, paramValues);
this.data = data;
}
案例
删除采购表数据,更新库存表。删除事件中可以获得删除数据的主键,根据主键获取数据,修改库存表的库存数,代码如下:
@EventListener(condition="#event.tableName=='main_buy' && #event.method=='delete' && #event.moduleName=='main'")
public void deleteStore(DbDeleteEvent event) throws Exception {
if(event.getStatus()!=Status.START) return;
String serviceName = ContextUtil.getServiceName();
//获取采购表的数据
QueryWrapper<Buy> wrapperBuy = DbrestUtil.createDeleteQueryWrapper(event);
List<Buy> listBuy = mapperBuy.selectList(wrapperBuy);
for(int i=0;i<listBuy.size();i++) {
Buy row = listBuy.get(i);
String fid = row.getFid();
String goods = row.getGoods();
Integer num = row.getNum();
//获取库存表的数据
DbrestWrapper<?> wrapperStore = (DbrestWrapper<?>)new DbrestWrapper<String>(serviceName, "main", "store");
wrapperStore.eq("goods",goods);
List<Store> listStore = DbrestUtil.selectList(wrapperStore, Store.class);
//减去库存数
Store store = new Store();
if(listStore.size()>0) {
Store s = listStore.get(0);
store.setFid(s.getFid());
store.setNum(s.getNum() - num);
store.setVersion(s.getVersion() + 1);
}
//更新到库存表
List<Store> storeList = new ArrayList<Store>();
storeList.add(store);
DbrestResult ret = DbrestUtil.upsert(serviceName, "main", "store", storeList);
}
}
查询数据的事件
事件参数
查询事件的参数为 DbQueryEvent 类,构造方法如下:
public DbQueryEvent(JSONObject request, Connection conn, String service, String tableName, String dataModelName, long offset, long limit, String queryField, List<String> conditionFields, List<String> conditionOperations, List<Object> paramValues) {
super(METHOD_QUERY, request, conn, service, tableName, dataModelName, conditionFields, conditionOperations, paramValues);
this.queryField = queryField;
this.offset = offset;
this.limit = limit;
}
案例
修改查询返回的数据。在查询事件中,获取查询结果数据,修改后返回,代码如下:
@EventListener(condition="#event.tableName=='main_buy' && #event.method=='query' && #event.moduleName=='main'")
public void queryBuy(DbQueryEvent event) throws Exception {
if(event.getStatus()!=Status.START) return;
//获得查询结果数据
QueryWrapper<Buy> wrapper = new QueryWrapper<>();
DbrestUtil.fillQueryWrapper(wrapper, event);
List<Buy> list = mapperBuy.selectList(wrapper);
//修改查询结果数据
for(int i=0;i<list.size();i++) {
list.get(i).setPrice(new BigDecimal(99));
}
//返回查询结果数据
JSONObject json = new JSONObject();
json.put("status", 200);
json.put("result", JSON.toJSONString(list));
json.put("eventHandled", true);
event.setResult(json);//设置返回数据
event.setHandled(true);//设置全接管
}