当前位置: 首页 > news >正文

seata的JacksonUndoLogParser回滚SerialArray类型的序列化和反序列化解析

引言

大家好,今天继续给大家带来seata相关代码的解析。今天讲到的是一个seata里面非常重要的一部分–事务的回滚,讲到事务的回滚,就必须讲到相关的数据是如何序列化和反序列化的,只有这部分正确运行,前镜像和后镜像才能正确,事务才能正确回滚。

那么今天就选了JacksonUndoLogParser来做解析,看看他是怎么样正确序列化和反序列化数据库的数组类型的。

什么是 SerialArray

  • SerialArray 是 Seata 自定义的一个类(org.apache.seata.rm.datasource.sql.serial.SerialArray),用于封装 JDBC 中的 java.sql.Array

  • 因为原生 java.sql.Array 无法直接被 Jackson 序列化,且不同数据库对数组的支持差异大,Seata 将其“扁平化”为一个可序列化的对象:

    • baseType:数组元素的 JDBC 类型(如 Types.INTEGER = 4
    • baseTypeName:数组元素的 SQL 类型名(如 "INTEGER" 或 "VARCHAR"
    • elements:实际的数组元素(Object[]

这样就能把数据库中的数组字段纳入 undo log,支持回滚。

简单讲讲JacksonUndoLogParser

它的核心作用是在分布式事务回滚过程中,BranchUndoLog 对象序列化为 JSON 字节数组(用于存储到数据库),以及从字节数组反序列化还原为 Java 对象(用于执行回滚)

实现接口

  • UndoLogParser:定义了 encode(BranchUndoLog) 和 decode(byte[]) 方法,用于 undo log 的编解码。
  • Initialize:提供初始化逻辑(通过 init() 方法注册自定义的序列化/反序列化器)

那么为什么要这么多自定义的序列化/反序列化器?我相信第一次看到代码的读者可能都会有这种疑惑
因为 BranchUndoLog 中可能包含 JDBC 特殊类型,如:

  • java.sql.Timestamp
  • java.sql.Blob / SerialBlob
  • java.sql.Clob / SerialClob
  • java.time.LocalDateTime
  • 达梦数据库的 DmdbTimestamp
  • 数组类型 SerialArray

这些类型 Jackson 默认无法正确处理(比如 Timestamp 的纳秒精度、Blob 的二进制内容等),所以必须提供自定义的序列化器与反序列化器且注册在Jackson的mapper里面,这样才可以正确处理这些类型。

mapper.registerModule(module);

关键配置

1.enableDefaultTyping用于启用 多态类型支持(Polymorphic Deserialization)。因为 BranchUndoLog 中的记录(如 TableRecords)可能包含子类(如 SqlUndoLog),需要保留类型信息以便正确反序列化。所以如果观察日志就会发现json格式里面保留了@Class

2.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)忽略未知字段,保证向前兼容(新版本加字段,旧版本仍能解析)

mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
mapper.enable(MapperFeature.PROPAGATE_TRANSIENT_MARKER);

如何序列化和反序列化SerialArray数组类型

我们上面已经说了SerialArray,那么现在就可以看看如何序列化和反序列化这种数组类型的

自定义序列化器(Java 对象 → JSON)

先自定义一个序列化器,继承JsonSerializer<T>,并重写里面三个方法。

serializeWithType(...)

  • 支持 多态序列化(Polymorphic Typing)
  • 当 Jackson 启用了 enableDefaultTyping(Seata 默认开启),会先写入类型标识(如 @class 字段)
  • 确保反序列化时能正确识别这是 SerialArray 而不是普通 Map
@Override
public void serializeWithType(SerialArray serialArray,JsonGenerator gen,SerializerProvider serializers,TypeSerializer typeSerializer)throws IOException {WritableTypeId typeIdDef =typeSerializer.writeTypePrefix(gen, typeSerializer.typeId(serialArray, JsonToken.START_OBJECT));serializeValue(serialArray, gen, serializers);typeSerializer.writeTypeSuffix(gen, typeIdDef);
}

serialize(SerialArray, JsonGenerator, ...)

这里是执行数组类型序列化的关键方法,先写入’{‘作为json数据的开始符号,再流式填充写入serialArray的内容,然后再以’}'作为json数据的结束符号。

这样就高效地完成了从对象到json格式的转换

@Override
public void serialize(SerialArray serialArray, JsonGenerator gen, SerializerProvider serializers)throws IOException {gen.writeStartObject();serializeValue(serialArray, gen, serializers);gen.writeEndObject();
}

serializeValue(...)

这里是将核心数据转换为json格式的重要方法。首先得先知道gen是核心实体,它负责生成json格式数据,还有很多通用方法用来生成一些固定格式[]或者{}等等。

在下面的代码核心体现就是写入SerialArray的相关数据,这很简单没啥好说的,值得注意的是,任何字段读取失败都写 null,避免整个 undo log 序列化失败。同时,元素类型自动处理:数字、字符串等由 gen.writeObject() 自动转为 JSON 值。了解这些就够了。

private void serializeValue(SerialArray serialArray, JsonGenerator gen, SerializerProvider serializers)throws IOException {gen.writeFieldName("baseType");try {gen.writeNumber(serialArray.getBaseType());} catch (SQLException e) {gen.writeNull();}gen.writeFieldName("baseTypeName");try {gen.writeString(serialArray.getBaseTypeName());} catch (SQLException e) {gen.writeNull();}gen.writeFieldName("elements");try {Object[] elements = serialArray.getElements();gen.writeStartArray();if (elements != null) {for (Object element : elements) {gen.writeObject(element);}}gen.writeEndArray();} catch (Exception e) {gen.writeNull();}
}

自定义反序列化器(JSON → Java 对象)

至于反序列化器就很简单了,只需要重写一个重要方法即可deserialize

根据json数据还原成SerialArray对象即可。回滚时,Seata 会根据 baseTypebaseTypeName 决定如何将 elements 转回数据库原生数组类型(通过 JDBC 驱动)

private static class SerialArrayDeserializer extends JsonDeserializer<SerialArray> {@Overridepublic SerialArray deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {try {JsonNode node = p.getCodec().readTree(p);SerialArray serialArray = new SerialArray();if (node.has("baseType") && !node.get("baseType").isNull()) {serialArray.setBaseType(node.get("baseType").asInt());}if (node.has("baseTypeName") && !node.get("baseTypeName").isNull()) {serialArray.setBaseTypeName(node.get("baseTypeName").asText());}if (node.has("elements") && node.get("elements").isArray()) {JsonNode elementsNode = node.get("elements");Object[] elements = new Object[elementsNode.size()];for (int i = 0; i < elementsNode.size(); i++) {JsonNode elementNode = elementsNode.get(i);if (elementNode.isNull()) {elements[i] = null;} else if (elementNode.isNumber()) {elements[i] = elementNode.asLong();} else if (elementNode.isTextual()) {elements[i] = elementNode.asText();} else {elements[i] = elementNode;}}serialArray.setElements(elements);}return serialArray;} catch (Exception e) {LOGGER.error("deserialize SerialArray error: {}", e.getMessage(), e);return null;}}
}

总结

上面就是JacksonUndoLogParser回滚SerialArray类型的序列化和反序列化解析了。只有正确序列化和反序列化,前后镜像(也可以称为快照)才能保证事务回滚。在回滚的时候,前镜像是回滚的目标,后镜像可以是回滚的标准(可以看成乐观锁),如果有人修改了数据导致与后镜像不一致,则先不回滚,会抛出相应异常来处理。

http://www.dtcms.com/a/619299.html

相关文章:

  • 在线做初中题网站网站设计昆明
  • Python中lambda函数与def函数的区别及应用场景
  • 找工作的一些感悟——前端小菜的成长
  • 注册好了域名怎么开始做网站网站建设 网站推广
  • 晋江企业网站建设在深圳注册一个公司需要多少钱
  • “开源AI智能名片链动2+1模式S2B2C商城小程序”在县级市场的应用与挑战分析
  • 大气网站案例网站制作公司备案
  • 16.触摸屏驱动
  • Q7: 在区块链上创建随机数有哪些挑战?
  • 宿城网站建设大连市网站制作电话
  • 做a视频网站有哪些系统优化的目的和意义
  • 网站开发外快如何优化网站打开速度
  • 城市配送联盟平台技术架构深度解析:赋能小 B 端全场景高效配送
  • 公司网站定制自己做网页怎么赚钱
  • 济南网站建设公司按需定制wordpress赞 踩插件
  • 建外贸网站的wordpress源码书籍
  • 织梦网站源码找一品资源企业网站建设费怎么账务处理
  • 如何在国外建设网站百度站长平台验证网站
  • 考研408--组成原理--day3--数字电路(补)加减乘除
  • 深圳企业网站建设推广服务wordpress extra script
  • Python的自述
  • OceanBase 常见异情况汇总
  • 江门网站建设方案开发wordpress云建站教程
  • 14.3 行业应用案例:金融、医疗、教育领域的定制化实践
  • 开发网站的基本原则做移动网站优化快
  • 人工智能技术- 语音语言- 03 ChatGPT 对话、写诗、写小说
  • 怎么制作网站上传太原建筑公司网站
  • 精密万向节能承受的最大扭矩是多少?
  • 游戏:ar从入门到落地
  • 深圳罗湖网站开发惠阳营销网站制作