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日

相关推荐

  • 3.3.6 枚举与数据字典

    枚举是大家在系统开发中经常用的一种类型,在oinone中也对枚举类型进行了支持,同时也做了相应的加强。希望通过本文能让大家对枚举的使用,有更全面的认知 一、枚举系统与数据字典 枚举是列举出一个有穷序列集的所有成员的程序。在元数据中,我们使用数据字典进行描述。 协议约定 枚举需要实现IEnum接口和使用@Dict注解进行配置,通过配置@Dict注解的dictionary属性来设置数据字典的唯一编码。前端使用枚举的displayName来展示,枚举的name来进行交互;后端使用枚举的value来进行交互(包括默认值设置也使用枚举的value)。 枚举会存储在元数据的数据字典表中。枚举分为两类:1.异常类;2.业务类。异常类枚举用于定义程序中的错误提示,业务类枚举用于定义业务中某个字段值的有穷有序集。 编程式用法 图3-3-6-1 编程式用法 如果一个字段的类型被定义为枚举,则该字段就可以使用该枚举来进行可选项约束(options)。该字段的可选项为枚举所对应数据字典的子集。 可继承枚举 继承BaseEnum可以实现java不支持的继承枚举。同时可继承枚举也可以用编程式动态创建枚举项。 可继承枚举也可以兼容无代码枚举。 图3-3-6-2 可继承枚举 二进制枚举 可以通过@Dict注解设置数据字典的bit属性或者实现BitEnum接口来标识该枚举值为2的次幂。 二、enum不可继承枚举(举例) 我们在介绍抽象基类中AbstractDemoCodeModel和AbstractDemoIdModel就引入了数据状态(DataStatusEnum)字段,并设置了必填和默认值为DISABLED。DataStatusEnum实现了IEnum接口,并用@Dict(dictionary = DataStatusEnum.dictionary, displayName = "数据状态")进行了注解。为什么不能继承呢?因为JAVA语言的限制导致enum是不可继承的 package pro.shushi.pamirs.core.common.enmu; import pro.shushi.pamirs.meta.annotation.Dict; import pro.shushi.pamirs.meta.common.enmu.IEnum; @Dict(dictionary = DataStatusEnum.dictionary, displayName = "数据状态") public enum DataStatusEnum implements IEnum<String> { DRAFT("DRAFT", "草稿", "草稿"), NOT_ENABLED("NOT_ENABLED", "未启用", "未启用"), ENABLED("ENABLED", "已启用", "已启用"), DISABLED("DISABLED", "已禁用", "已禁用"); public static final String dictionary = "partner.DataStatusEnum"; private String value; private String displayName; private String help; DataStatusEnum(String value, String displayName, String help) { this.value = value; this.displayName = displayName; this.help = help; } public String getValue() { return value; } public String getDisplayName() { return displayName; } public String getHelp() { return help; } } 图3-3-6-3 不可继承枚举 三、BaseEnum可继承枚举(举例) Step1 新增CatShapeExEnum继承CatShapeEnum枚举 package pro.shushi.pamirs.demo.api.enumeration; import pro.shushi.pamirs.meta.annotation.Dict; @Dict(dictionary = CatShapeExEnum.DICTIONARY,displayName = "萌猫体型Ex") public class CatShapeExEnum extends CatShapeEnum { public static final String DICTIONARY ="demo.CatShapeExEnum"; public final static CatShapeExEnum MID =create("MID",3,"中","中"); } 图3-3-6-4 新增CatShapeExEnum继承CatShapeEnum枚举 Step2 修改PetCatType的shape字段类型为CatShapeExEnum package pro.shushi.pamirs.demo.api.model; import pro.shushi.pamirs.demo.api.enumeration.CatShapeExEnum; import pro.shushi.pamirs.meta.annotation.Field; import pro.shushi.pamirs.meta.annotation.Model; @Model.MultiTableInherited(type = PetCatType.KIND_CAT) @Model.model(PetCatType.MODEL_MODEL) @Model(displayName="萌猫品种",labelFields = {"name"}) public class PetCatType extends PetType { public static final String MODEL_MODEL="demo.PetCatType"; public static final String KIND_CAT="CAT"; @Field(displayName = "宠物分类",defaultValue…

    2024年5月23日
    3.8K00
  • 4.1.22 框架之分布式缓存

    分布式缓存Oinone平台主要用到了Redis,为了让业务研发时可以无感使用RedisTemplate和StringRedisTemplate,已经提前注册好了redisTemplate和stringRedisTemplate,而且内部会自动处理相关特殊逻辑以应对多租户环境,小伙伴不能自己重新定义Redis的相关bean。 使用说明 配置说明 spring: redis: database: 0 host: 127.0.0.1 port: 6379 timeout: 2000 # cluster: # nodes: # – 127.0.0.1:6379 # timeout: 2000 # max-redirects: 7 jedis: pool: # 连接池中的最大空闲连接 默认8 max-idle: 8 # 连接池中的最小空闲连接 默认0 min-idle: 0 # 连接池最大连接数 默认8 ,负数表示没有限制 max-active: 8 # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认-1 max-wait: -1 图4-1-22-1 分布式缓存配置说明 代码示例 package pro.shushi.pamirs.demo.core.service; import org.springframework.stereotype.Component; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate; @Component public class Test { @Autowired private RedisTemplate redisTemplate; @Autowired private StringRedisTemplate stringRedisTemplate } 图4-1-22-2 代码示例

    Oinone 7天入门到精通 2024年5月23日
    2.1K00
  • 页面设计

    1. 功能说明 页面设计时界面设计器中「页面」模块的设计入口,在这个界面,进行页面的设置、搭建、设计、排版。 主要分为顶部操作栏、左侧工具栏、中部设计画布区、右侧属性面板。 2. 操作栏 进入页面设计,顶部显示了页面的标题,以及返回、发布等操作。 2.1 发布 页面设计完成后,点击「发布」运行页面生效。若不点击发布,页面也有自动保存的功能,但在发布前,自动保存也等同于草稿,不会正式生效。 发布时如果有属性不符合校验规则(必填的属性未填、输入的内容校验不通过),会发布失败,相应的字段会特殊标记,需要查看并修改属性。 2.2 显示/隐藏母版 进入页面设计时默认不展示母版,可以手动操作显示母版 3. 工具栏 左侧的工具栏中包括组件库、页面设置等模块。 3.1 组件库 组件库中包含组件和模型,组件是当前设计器支持的所有组件,模型是页面所在模型下的所有字段和动作。 3.1.1 组件 组件中展示了系统支持的所有组件。包含 1)布局类组件,如分组、选项卡等,使用布局组件可以将页面进字段分类、分页; 2)字段类组件,如单行文本、整数、日期等等,使用字段类组件时都会在模型下对应创建一个字段; 3)动作类组件,如跳转动作、提交动作、链接动作等。 3.1.2 模型 组件库顶部,由组件可切换为模型:模型选项下,会展示当前模型的所有字段,以及系统默认动作。可以直接拖拽字段至设计画布中,会应用形成某个组件,可对组件进行多样的属性设置,优化交互。 3.1.2 组件和模型有什么区别 1展示内容维度不同 组件中展示的内容是组件信息,如分组、选项卡、单行文本、文件上传等;模型中展示的是模型下已有的所有字段。 2使用功能不同 组件中的组件使用前需要在模型中创建一个字段,当然,创建好的字段也会存在于模型中;模型中的字段可直接使用,并且使用时会在设计画布中对应生成个默认组件。 3使用场景不同 如果模型中已经存在目标字段,应直接选择从模型中拖拽字段;如果模型中没有需要的字段,可以在页面中增加一个组件,实际上也是在新增一个字段。 3.2 页面设置 页面设置中可以修改当前页面的标题、分组、页面描述,同时也是给页面上传缩略图的唯一入口。 4. 设计画布 将组件或字段拖拽至设计画布区,会生成样式。点击组件,右侧可对其进行设置,大部分属性可实时在画布中展示效果。 5. 属性面板 右侧属性面板抽屉中可以设置属性或查看字段信息,通过不同的属性配置化实现组件的多样化。 5.1 属性 属性中包括基本信息(如标题、占位提示、描述说明等)、校验信息(如是否必填、长度校验等)、交互信息(如排序方式、是否展示计数器等) 5.2 字段 首次从组件中拖拽,会先展示字段信息,并且是需要先创建一个字段,字段创建成功后,再次切换字段选项,只读展示当前组件所对应的字段基本信息。同理,如果是直接从字段中拖拽,字段选项中,只读展示当前字段基本信息。

    2024年6月20日
    4.3K00
  • 3.5 Oinone以交互为外在

    交互组件(UI Componment): 用组件化的方式统一管理:菜单、布局、视图 用Action做衔接,来勾绘出模块的前端交互拓扑,描述所有可操作行为 oinone的页面路由串联规则是以菜单为导航,用Action做衔接,用View做展示,详见3.5.2.1View的【整体介绍】。 本章节会重点介绍: 如何定义菜单、视图、行为以及其初始化 xml配置:视图配置(包括字段联动)、字段配置、布局配置、动作配置 前端组件自定义

    Oinone 7天入门到精通 2024年5月23日
    1.4K00

Leave a Reply

Please Login to Comment