4.1.8 函数之事务管理

一、事务管理介绍

函数Function支持事务字段为isTransaction(默认为false),事务传播行为propagationBehavior(默认PROPAGATION_SUPPORTS),事务隔离级别isolationLevel(默认使用数据库默认的事务隔离级别),所以不会默认为函数添加事务。另外事务配置提供全局配置。

平台事务管理兼容Spring声明式与编程式事务,支持多数据源事务管理。事务管理中多数据源嵌套独立事务,不会造成死锁风险。使用多数据源或分表操作,不会导致脏读。如果需要多数据源分布式事务,请使用PamirsTransational分布式事务管理方案(@PamirsTransational(enableXa=true))。分布式事务一般用于量小的跨模块配置管理场景

使用方式

  • 声明式事务,使用@PamirsTransactional注解在需要事务管理的类或方法上标注。在非无代码场景下,与@Transactional注解功能一致。
  • 编程式事务,使用PamirsTransactionTemplate即可。在非无代码场景下,与TransactionTemplate功能一致。
  • 配置式事务,使用TxConfig模型在模块安装时初始化存储事务配置数据。

事务特性

  • 原子性 (atomicity):强调事务的不可分割.
  • 一致性 (consistency):事务的执行的前后数据的完整性保持一致.
  • 隔离性 (isolation):一个事务执行的过程中,不应该受到其他事务的干扰
  • 持久性(durability) :事务一旦结束,数据就持久到数据库

事务隔离级别

事务隔离级别指的是一个事务对数据的修改与另一个并行的事务的隔离程度,当多个事务同时访问相同数据时,如果没有采取必要的隔离机制,就可能发生以下问题:

问题 描述
脏读 一个事务读到另一个事务未提交的更新数据,所谓脏读,就是指事务A读到了事务B还没有提交的数据,比如银行取钱,事务A开启事务,此时切换到事务B,事务B开启事务–>取走100元,此时切换回事务A,事务A读取的肯定是数据库里面的原始数据,因为事务B取走了100块钱,并没有提交,数据库里面的账务余额肯定还是原始余额,这就是脏读
不可重复读 在一个事务里面的操作中发现了未被操作的数据 比方说在同一个事务中先后执行两条一模一样的select语句,期间在此次事务中没有执行过任何DDL语句,但先后得到的结果不一致,这就是不可重复读
幻读 是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。 同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象 发生了幻觉一样。

表4-1-8-1 事务隔离级别

Pamirs(Spring)支持的隔离级别

隔离级别 描述
DEFAULT 使用数据库本身使用的隔离级别 ORACLE(读已提交) MySQL(可重复读)
READ_UNCOMITTED 读未提交(脏读)最低的隔离级别,一切皆有可能。
READ_COMMITED 读已提交,ORACLE默认隔离级别,有不可重复读以及幻读风险。
REPEATABLE_READ 可重复读,解决不可重复读的隔离级别,但还是有幻读风险。
SERLALIZABLE 串行化,最高的事务隔离级别,不管多少事务,挨个运行完一个事务的所有子事务之后才可以执行另外一个事务里面的所有子事务,这样就解决了脏读、不可重复读和幻读的问题了

表4-1-8-2 隔离级别与描述

隔离级别 脏读可能性 不可重复读可能性 幻读可能性 加锁度
READ_UNCOMITTED
READ_COMMITED
REPEATABLE_READ
SERLALIZABLE

表4-1-8-3 隔离级别说明表

事务的传播行为

  • 保证同一个事务中

  • PROPAGATION_REQUIRED 支持当前事务,如果不存在 就新建一个(默认)

  • PROPAGATION_SUPPORTS 支持当前事务,如果不存在,就不使用事务

  • PROPAGATION_MANDATORY 支持当前事务,如果不存在,抛出异常

  • 保证没有在同一个事务中

  • PROPAGATION_REQUIRES_NEW 如果有事务存在,挂起当前事务,创建一个新的事务

  • PROPAGATION_NOT_SUPPORTED 以非事务方式运行,如果有事务存在,挂起当前事务

  • PROPAGATION_NEVER 以非事务方式运行,如果有事务存在,抛出异常

  • PROPAGATION_NESTED 如果当前事务存在,则嵌套事务执行

A中嵌套B事务,嵌套PROPAGATION_REQUIRES_NEW方法勿与A在同类中。

异常状态 PROPAGATION_REQUIRES_NEW (两个独立事务) PROPAGATION_NESTED (B的事务嵌套在A的事务中) PROPAGATION_REQUIRED (同一个事务)
A抛异常 B正常 A回滚,B正常提交 A与B一起回滚 A与B一起回滚
A正常 B抛异常 1.如果A中捕获B的异常,并没有继续向上抛异常,则B先回滚,A再正常提交; 2.如果A未捕获B的异常,默认则会将B的异常向上抛,则B先回滚,A再回滚 B先回滚,A再正常提交 A与B一起回滚
A抛异常B抛异常 B先回滚,A再回滚 A与B一起回滚 A与B一起回滚
A正常 B正常 B先提交,A再提交 A与B一起提交 A与B一起提交

表4-1-8-4 事务传播行为

二、声明式事务(举例)

Step1 修改PetShopBatchUpdateAction

用@PamirsTransactional或者@Transactional注解来声明事务,PamirsTransactional跟Spring的Transactional区别在于PamirsTransactional支持多库事务,但此多库事务为非严格的分布式多库事务,之所以选择这个方案,原因如下

a. 不损害任何性能。

b. 事务保障率超过4个9

c. 经过阿里的大厂验证,特别是在阿里的结算平台中得到了很好的验证

@PamirsTransactional更多配置项请详见4.1.7【函数之元数据详解】一文,自己多试试。同时@PamirsTransactional百分百兼容@Transactional

@Action(displayName = "确定",bindingType = ViewTypeEnum.FORM,contextType = ActionContextTypeEnum.SINGLE)
@PamirsTransactional
//@Transactional
public PetShopBatchUpdate conform(PetShopBatchUpdate data){
    if(data.getPetShopList() == null || data.getPetShopList().size()==0){
        throw  PamirsException.construct(DemoExpEnumerate.PET_SHOP_BATCH_UPDATE_SHOPLIST_IS_NULL).errThrow();
    }
    List<PetShopProxy> proxyList = data.getPetShopList();
    for(PetShopProxy petShopProxy:proxyList){
        petShopProxy.setDataStatus(data.getDataStatus());
    }
    new PetShopProxy().updateBatch(proxyList);
    throw PamirsException.construct(DemoExpEnumerate.SYSTEM_ERROR).errThrow();
    //        return data;
}

图4-1-8-1 修改PetShopBatchUpdateAction

Step2 重启看效果

进入店铺管理列表页,选择记录点击【批量更新数据状态】按钮,修改记录的数据状态为【未启用】,提交看效果。期望效果为:提示系统异常,数据修改失败

image.png

图4-1-8-2 数据状态显示已启用

image.png

图4-1-8-3 批量更新数据状态

image.png

图4-1-8-4 提示系统异常

三、编程式事务(举例)

为了提升性能,特别是在高并发场景,编程式事务开发模式有利于精细化控制事务开启长度,尽可能地在事务开启前,把费时的查询工作、数据准备做完。基本套路如下

Tx.build(new TxConfig().setPropagation(Propagation.REQUIRED.value())).executeWithoutResult(status -> {
      //执行逻辑
});

图4-1-8-5 编程式事务代码示意

Step1 修改PetShopBatchUpdateAction

@Action(displayName = "确定",bindingType = ViewTypeEnum.FORM,contextType = ActionContextTypeEnum.SINGLE)
public PetShopBatchUpdate conform(PetShopBatchUpdate data){
    if(data.getPetShopList() == null || data.getPetShopList().size()==0){
        throw  PamirsException.construct(DemoExpEnumerate.PET_SHOP_BATCH_UPDATE_SHOPLIST_IS_NULL).errThrow();
    }
    List<PetShopProxy> proxyList = data.getPetShopList();
    for(PetShopProxy petShopProxy:proxyList){
        petShopProxy.setDataStatus(data.getDataStatus());
    }
    Tx.build(new TxConfig().setPropagation(Propagation.REQUIRED.value())).executeWithoutResult(status -> {
        new PetShopProxy().updateBatch(proxyList);
        throw PamirsException.construct(DemoExpEnumerate.SYSTEM_ERROR).errThrow();
    });
    return data;
}

图4-1-8-6 修改PetShopBatchUpdateAction

Step2 重启看效果

跟声明式事务一致的效果

四、配置式事务

略,该模式一般用于平台内部使用以及无代码编辑器管理事务时用到,就不举例了

分布式事务(不建议使用)

如果要严格意义上的分布式事务,需要配置enableXa为true,@PamirsTransational(enableXa=true)。同时引入依赖包

<groupId>pro.shushi.pamirs.framework</groupId>
<artifactId>pamirs-connectors-data-xa</artifactId>

图4-1-8-7 分布式事务依赖包

注:该版本还不支持远程RPC后的分布式事务,因该模式有很大的弊端,也就是把原本无状态的服务变成有状态,导致性能和耦合度都极差。所以我们一般使用事务性消息、异步任务等最终一致性方案去替代。

Oinone社区 作者:史, 昂原创文章,如若转载,请注明出处:https://doc.oinone.top/oio4/9283.html

访问Oinone官网:https://www.oinone.top获取数式Oinone低代码应用平台体验

Like (0)
史, 昂's avatar史, 昂数式管理员
Previous 2024年5月23日 am8:55
Next 2024年5月23日

相关推荐

  • 4.1.23 框架之信息传递

    在4.1.13【Action之校验】、3.4.1【构建第一个Function】等文章中,都用到PamirsSession.getMessageHub()来设置返回消息,基本上都是在传递后端逻辑判断的异常信息,而且在系统报错时也会通过它来返回错误信息,前端接收到错误信息则会以提示框的方式进行错误提示。其实后端除了可以返回错误信息以外,还可以返回调试、告警、成功、信息等级别的信息给前端。但是默认情况下前端只提示错误信息,可以通过前端的统一配置放开提示级别,有点类似后端的日志级别。 一、不同信息类型的举例 Step1 新建PetTypeAction 借用PetType模型的表格页做为信息传递的测试入口,为PetType模型新增一个ServerAction,在代码中对信息的所有类型进行模拟 package pro.shushi.pamirs.demo.core.action; import org.springframework.stereotype.Component; import pro.shushi.pamirs.demo.api.model.PetCatItem; import pro.shushi.pamirs.demo.api.model.PetType; import pro.shushi.pamirs.meta.annotation.Action; import pro.shushi.pamirs.meta.annotation.Model; import pro.shushi.pamirs.meta.api.dto.common.Message; import pro.shushi.pamirs.meta.api.session.PamirsSession; import pro.shushi.pamirs.meta.enmu.ActionContextTypeEnum; import pro.shushi.pamirs.meta.enmu.InformationLevelEnum; import pro.shushi.pamirs.meta.enmu.ViewTypeEnum; @Model.model(PetType.MODEL_MODEL) @Component public class PetTypeAction { @Action(displayName = "消息",bindingType = ViewTypeEnum.TABLE,contextType = ActionContextTypeEnum.CONTEXT_FREE) public PetType message(PetType data){ PamirsSession.getMessageHub().info("info1"); PamirsSession.getMessageHub().info("info2"); PamirsSession.getMessageHub().error("error1"); PamirsSession.getMessageHub().error("error2"); PamirsSession.getMessageHub().msg(new Message().msg("success1").setLevel(InformationLevelEnum.SUCCESS)); PamirsSession.getMessageHub().msg(new Message().msg("success2").setLevel(InformationLevelEnum.SUCCESS)); PamirsSession.getMessageHub().msg(new Message().msg("debug1").setLevel(InformationLevelEnum.DEBUG)); PamirsSession.getMessageHub().msg(new Message().msg("debug2").setLevel(InformationLevelEnum.DEBUG)); PamirsSession.getMessageHub().msg(new Message().msg("warn1").setLevel(InformationLevelEnum.WARN)); PamirsSession.getMessageHub().msg(new Message().msg("warn2").setLevel(InformationLevelEnum.WARN)); return data; } } 图4-1-23-1 为PetType模型新增一个ServerAction Step2 (前端)修改提示级别 在项目初始化时使用CLI构建初始化前端工程,在src/middleware有拦截器的默认实现,修改信息提示的默认级别为【ILevel.SUCCESS】 图4-1-23-2(前端)修改提示级别 const DEFAULT_MESSAGE_LEVEL = ILevel.SUCCESS; 图4-1-23-3(前端)修改提示级别 Step3 重启系统看效果 从页面效果中看到已经不在是只提示错误信息。从协议端看错误级别的信息是在errors下,其他级别的信息是在extensions下。 图4-1-23-4 示例效果 图4-1-23-5 系统提示的返回结果 二、MessageHub的其他说明 是实现上看MessageHub是基于GQL协议,前后端都有配套实现。同时前端还提供了订阅MessageHub的信息功能,以满足前端更多交互要求,前端MessageHub提供的订阅能力使用教程详见4.2.2前端高级特性之【框架之MessageHub】一文。

    2024年5月23日
    1.3K00
  • 4.1.24 框架之分库分表

    随着数据库技术的发展如分区设计、分布式数据库等,业务层的分库分表的技术终将成老一辈程序员的回忆,谈笑间扯扯蛋既羡慕又自吹地说到“现在的研发真简单,连分库分表都不需要考虑了”。竟然这样为什么要写这篇文章呢?因为现今的数据库虽能解决大部分场景的数据量问题,但涉及核心业务数据真到过亿数据后性能加速降低,能给的方案都还有一定的局限性,或者说性价比不高。相对性价比比较高的分库分表,也会是现阶段一种不错的补充。言归正传oinone的分库分表方案是基于Sharding-JDBC的整合方案,所以大家得先具备一点Sharding-JDBC的知识。 一、分表(举例) 做分库分表前,大家要有一个明确注意的点就是分表字段的选择,它是非常重要的,与业务场景非常相关。在明确了分库分表字段以后,甚至在功能上都要做一些妥协。比如分库分表字段在查询管理中做为查询条件是必须带上的,不然效率只会更低。 Step1 新建ShardingModel模型 ShardingModel模型是用于分表测试的模型,我们选定userId作为分表字段。分表字段不允许更新,所以这里更新策略设置类永不更新,并在设置了在页面修改的时候为readonly package pro.shushi.pamirs.demo.api.model; import pro.shushi.pamirs.boot.base.ux.annotation.field.UxWidget; import pro.shushi.pamirs.boot.base.ux.annotation.view.UxForm; import pro.shushi.pamirs.meta.annotation.Field; import pro.shushi.pamirs.meta.annotation.Model; import pro.shushi.pamirs.meta.enmu.FieldStrategyEnum; @Model.model(ShardingModel.MODEL_MODEL) @Model(displayName = "分表模型",summary="分表模型",labelFields ={"name"} ) public class ShardingModel extends AbstractDemoIdModel { public static final String MODEL_MODEL="demo.ShardingModel"; @Field(displayName = "名称") private String name; @Field(displayName = "用户id",summary = "分表字段",immutable=true/* 不可修改 **/) @UxForm.FieldWidget(@UxWidget(readonly = "scene == 'redirectUpdatePage'"/* 在编辑页面只读 **/ )) @Field.Advanced(updateStrategy = FieldStrategyEnum.NEVER) private Long userId; } 图4-1-24-1 新建ShardingModel模型 Step2 配置分表策略 配置ShardingModel模型走分库分表的数据源pamirsSharding 为pamirsSharding配置数据源以及sharding规则 a. pamirs.sharding.define用于oinone的数据库表创建用 b. pamirs.sharding.rule用于分表规则配置 pamirs: load: sessionMode: true framework: system: system-ds-key: base system-models: – base.WorkerNode data: default-ds-key: pamirs ds-map: base: base modelDsMap: "[demo.ShardingModel]": pamirsSharding #配置模型对应的库 图4-1-24-2 指定模型对应数据源 pamirs: sharding: define: data-sources: ds: pamirs pamirsSharding: pamirs #申明pamirsSharding库对应的pamirs数据源 models: "[trigger.PamirsSchedule]": tables: 0..13 "[demo.ShardingModel]": tables: 0..7 table-separator: _ rule: pamirsSharding: #配置pamirsSharding库的分库分表规则 actual-ds: – pamirs #申明pamirsSharding库对应的pamirs数据源 sharding-rules: # Configure sharding rule ,以下配置跟sharding-jdbc配置一致 – tables: demo_core_sharding_model: #demo_core_sharding_model表规则配置 actualDataNodes: pamirs.demo_core_sharding_model_${0..7} tableStrategy: standard: shardingColumn: user_id shardingAlgorithmName: table_inline shardingAlgorithms: table_inline: type: INLINE props: algorithm-expression: demo_core_sharding_model_${(Long.valueOf(user_id) % 8)} props: sql.show: true 图4-1-24-3 分库分表规则配置 Step3 配置测试入口 修改DemoMenus类增加一行代码,为测试提供入口 @UxMenu("分表模型")@UxRoute(ShardingModel.MODEL_MODEL) class ShardingModelMenu{} 图4-1-24-4 配置测试入口 Step4 重启看效果 自行尝试增删改查 观察数据库表与数据分布 图4-1-24-5 自行尝试增删改查 图4-1-24-6 观察数据库表与数据分布 二、分库分表(举例) Step1 新建ShardingModel2模型 ShardingModel2模型是用于分库分表测试的模型,我们选定userId作为分表字段。分库分表字段不允许更新,所以这里更新策略设置类永不更新,并在设置了在页面修改的时候为readonly package pro.shushi.pamirs.demo.api.model; import pro.shushi.pamirs.boot.base.ux.annotation.field.UxWidget; import pro.shushi.pamirs.boot.base.ux.annotation.view.UxForm; import…

    2024年5月23日
    1.4K00
  • 4.1.18 框架之网关协议-Variables变量

    我们在应用开发过程有一种特殊情况在后端逻辑编写的时候需要知道请求的发起入口,平台利用GQL协议中的Variables属性来传递信息,本文就介绍如何获取。 一、前端附带额外变量 属性名 类型 说明 scene String 菜单入口 表4-1-18-1 前端附带额外变量 图4-1-18-1 variables信息中的scene 二、后端如何接收variables信息 通过PamirsSession.getRequestVariables()可以得到PamirsRequestVariables对象。 三、第一个variable(举例) Step1 修改PetTalentAction,获取得到前端传递的Variables package pro.shushi.pamirs.demo.core.action; ……类引用 @Model.model(PetTalent.MODEL_MODEL) @Component public class PetTalentAction { ……其他代码 @Function.Advanced(type= FunctionTypeEnum.QUERY) @Function.fun(FunctionConstants.queryPage) @Function(openLevel = {FunctionOpenEnum.API}) public Pagination<PetTalent> queryPage(Pagination<PetTalent> page, IWrapper<PetTalent> queryWrapper){ String scene = (String)PamirsSession.getRequestVariables().getVariables().get("scene"); System.out.println("scene: "+ scene); ……其他代码 } ……其他代码 } 图4-1-18-2 修改PetTalentAction Step2 重启验证 点击宠物达人不同菜单入口,查看效果 图4-1-18-3 示例效果(一) 图4-1-18-4 示例效果(二)

    2024年5月23日
    1.4K00
  • 业务审计

    整体介绍 业务审计是指通过跟踪用户在平台业务应用中的各类行为操作以及关联的数据变化,支撑企业业务审计所需。平台提供可视化审计规则配置,根据审计规则记录行为日志,其中日志包括登录日志、应用日志; 登录日志:记录平台用户登录行为。 应用日志:针对已订阅的审计规则记录用户操作信息,是用户在各应用中操作行为留痕记录。 审计规则:业务审计中,数据变化订阅记录的规则,是记录应用日志的规则。 操作入口:应用中心——业务审计应用。

    Oinone 7天入门到精通 2024年6月20日
    1.8K00
  • 数据字典

    1. 什么是数据字典 数据字典是一些固定字典项的集合,可作为多选或单选的选项,例如人员性别、员工属相、杭州市下属的行政区等都是数据字典。 选择一个应用/模块下的一个小模块,可以查看其中包含的自定义字典(自建的数据字典)和系统字典。可以通过导入或添加创建自定义字典。 2. 数据字典基本操作 删除:系统字典不支持删除,无法批量删除自定义数据字典,若数据字典已经被字段、页面等引用时,也无法删除这个数据字典。 隐藏/可见:自定义字典、系统字典都可以操作隐藏和可见,隐藏和可见不影响数据字典在字段、页面等处的使用。 查看引用关系:点击查看引用关系,展开弹窗,展示该数据字典和字段、视图的引用关系。 修改:若数据字典已经被引用,无法删除其中的字典项。若无引用关系则可以任意修改。 3. 数据字典导入 点击“导入数据字典”按钮,弹出窗口,可根据图中引导进行数据字典的导入。 在经典模式下,导入数据字典会新建数据字典。 在专家模式下,数据字典导入的模板中包含字段“字典编码”,若导入的字典编码不存在则会新建数据字典,若导入的字典编码已存在则会修改字典编码的设置,新建和修改的动作会校验是否符合规范。 4. 数据字典创建 4.1 专家模式(低代码模式) 字典项类型有“二进制、文本、整数”三种可选,选择二进制时,字典项值需要选择存储在数据库二进制中的第几位。系统根据字典项值去查找字典项名称,因此字典项值不可重复。 数据字典的api名称、代码名称、描述可不填,部分系统会赋默认值。 字典项中字典项名称和字典项值必填,api名称和字典项描述不填系统会赋默认值。 4.2 经典模式(无代码模式) 经典模式下只需要填写字典名称,添加字典项即可。系统会自动将字典项类型设置为“二进制”,并将字典项按照创建的先后顺序设置字典项值的位数。字典项类型、字典项值释义可参照专家模式的解释。

    2024年6月20日
    2.0K00

Leave a Reply

Please Login to Comment