4.1.5 模型之持久层配置

一、批量操作

批量操作包括批量创建与批量更新。批量操作的提交类型系统默认值为batchCommit。

批量提交类型:

  • useAffectRows,循环单次单条脚本提交,返回实际影响行数
  • useAndJudgeAffectRows,循环单次单条脚本提交,返回实际影响行数,若实际影响行数与输入不一致,抛出异常
  • collectionCommit,将多个单条更新脚本拼接成一个脚本提交,不能返回实际影响行数
  • batchCommit,使用单条更新脚本批量提交,不能返回实际影响行数。

全局配置

pamirs:
    mapper:
    batch: batchCommit

图4-1-5-1 全局配置

运行时配置

非乐观锁模型系统默认采用batchCommit提交更新操作;乐观锁模型默认采用useAndJudgeAffectRows提交更新操作。也可以使用以下方式在运行时改变批量提交方式。

Spider.getDefaultExtension(BatchApi.class).run(() -> {
    更新逻辑
}, 批量提交类型枚举);

图4-1-5-2 运行时配置

运行时校正

如果模型配置了数据库自增主键,而批量新增的批量提交类型为batchCommit,则系统将批量提交类型变更为collectionCommit(如果使用batchCommit,则需要单条提交以获得正确的主键返回值,性能有所损失)。

如果模型配置了乐观锁,而批量更新的批量提交类型为collectionCommit或者batchCommit,则系统将批量提交类型变更为useAndJudgeAffectRows。也可以失效乐观锁,让系统不做批量提交类型变更处理。

二、乐观锁(举例)

在一些会碰到并发修改的数据,往往需要进行并发控制,一般数据库层面有两种一种是悲观锁、一种是乐观锁。oinone对乐观锁进行了良好支持

定义方式

乐观锁的两种定义方式:

  • 通过快捷继承VersionModel,构建带有乐观锁,唯一编码code且主键为id的模型。

  • 可以在字段上使用@Field.Version注解来标识该模型更新数据时使用乐观锁

如果更新的实际影响行数与入参数量不一致,则会抛出异常,错误码为10150024。如果是批量更新数据,为了返回准确的实际影响行数,批量更新由批量提交改为循环单条数据提交更新,性能有所损失。

失效乐观锁

一个模型在某些场景下需要使用乐观锁来更新数据,而另一些场景不需要使用乐观锁来更新数据,则可以使用以下方式在一些场景下失效乐观锁。更多元位指令用法详见4.1.9【函数之元位指令】一文。

PamirsSession.directive().disableOptimisticLocker();
try{
    更新逻辑
} finally {
    PamirsSession.directive().enableOptimisticLocker();
}

图4-1-5-3 失效乐观锁

不抛乐观锁异常

将批量提交类型设置为useAffectRows即可,这样可改由外层逻辑对返回的实际影响行数进行自主判断。

Spider.getDefaultExtension(BatchApi.class).run(() -> {
    更新逻辑,返回实际影响行数
}, BatchCommitTypeEnum.useAffectRows);

图4-1-5-4 将批量提交类型设置为useAffectRows

构建第一个VersionModel

Step1 新建PetItemInventroy模型,继承快捷模型VersionModel

package pro.shushi.pamirs.demo.api.model;

import pro.shushi.pamirs.meta.annotation.Field;
import pro.shushi.pamirs.meta.annotation.Model;
import pro.shushi.pamirs.meta.base.common.VersionModel;

import java.math.BigDecimal;

@Model.model(PetItemInventroy.MODEL_MODEL)
@Model(displayName = "宠物商品库存",summary="宠物商品库存",labelFields = {"itemName"})
public class PetItemInventroy extends VersionModel {
    public static final String MODEL_MODEL="demo.PetItemInventroy";

    @Field(displayName = "商品名称")
    private String itemName;

    @Field(displayName = "库存数量")
    private BigDecimal quantity;

}

图4-1-5-5 新建PetItemInventroy模型

Step2 修改DemoMenu,增加访问入口

@UxMenu("商品库存")@UxRoute(PetItemInventroy.MODEL_MODEL) class PetItemInventroyMenu{}

图4-1-5-6 修改DemoMenu

Step3 重启看效果

体验一:页面上新增、修改数据库字段中的opt_version会自动加一

image.png

图4-1-5-7 示例效果一

image.png

图4-1-5-8 示例效果二

image.png

图4-1-5-9 示例效果三

image.png

图4-1-5-10 示例效果四

体验二:同时打两个页面,依次点击,您会发现一个改成功,一个没有改成功。但页面都没有报错,只是update返回影响行数一个为1,另一个为0而已。

image.png

图4-1-5-11 编辑宠物商品库存

image.png

图4-1-5-12 宠物商品库存列表

注:增加了乐观锁,我们在写代码的时候一定要注意,单记录更新操作的时候要去判断返回结果(影响行数),不然没改成功,程序是不会抛错的。不像batch接口默认会报错

Step4 预留任务:重写PetItemInventroy的update函数

留个任务,请各位小伙伴自行测试玩玩,这样会更有体感

package pro.shushi.pamirs.demo.core.action;

import org.springframework.stereotype.Component;
import pro.shushi.pamirs.demo.api.enumeration.DemoExpEnumerate;
import pro.shushi.pamirs.demo.api.model.PetItemInventroy;
import pro.shushi.pamirs.demo.api.model.PetTalent;
import pro.shushi.pamirs.meta.annotation.Function;
import pro.shushi.pamirs.meta.annotation.Model;
import pro.shushi.pamirs.meta.common.exception.PamirsException;
import pro.shushi.pamirs.meta.constant.FunctionConstants;
import pro.shushi.pamirs.meta.enmu.FunctionOpenEnum;
import pro.shushi.pamirs.meta.enmu.FunctionTypeEnum;

import java.util.ArrayList;
import java.util.List;

@Model.model(PetItemInventroy.MODEL_MODEL)
@Component
public class PetItemInventroyAction {

    @Function.Advanced(type= FunctionTypeEnum.UPDATE)
    @Function.fun(FunctionConstants.update)
    @Function(openLevel = {FunctionOpenEnum.API})
    public PetItemInventroy update(PetItemInventroy data){
        List<PetItemInventroy> inventroys = new ArrayList<>();
        inventroys.add(data);
        //批量更新会,自动抛错
        int i = data.updateBatch(inventroys);
        //单记录更新,不自动抛售需要自行判断
//        int i = data.updateById();
//        if(i!=1){
//            throw PamirsException.construct(DemoExpEnumerate.PET_ITEM_INVENTROY_UPDATE_VERSION_ERROR).errThrow();
//        }
        return data;
    }

}

图4-1-5-13 重写PetItemInventroy的update函数

注意事项:

如果是自定义页面需要把optVersion字段配置到视图的xml中去,可以配置为隐藏 。例子中因为默认页面已经自动带上了optVersion,所以容易被人忽视掉。

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

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

(0)
史, 昂史, 昂
上一篇 2024年5月23日 am8:58
下一篇 2024年5月23日

相关推荐

  • 4.1.15 框架之网关协议

    一、多端协议 协议内容格式 请求头 头信息 headerMap "sec-fetch-mode" -> "cors" "content-length" -> "482" "sec-fetch-site" -> "none" "accept-language" -> "zh-CN,zh;q=0.9" "cookie" -> "pamirs_uc_session_id=241af6a1dbba41a4b35afc96ddf15915" "origin" -> "chrome-extension://flnheeellpciglgpaodhkhmapeljopja" "accept" -> "application/json" "host" -> "127.0.0.1:8090" "connection" -> "keep-alive" "content-type" -> "application/json" "accept-encoding" -> "gzip, deflate, br" "user-agent" -> "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.192 Safari/537.36" "sec-fetch-dest" -> "empty" 图4-1-15-1 头信息 headerMap 请求地址 requestUrl 例如 http://127.0.0.1:8090/pamirs/DemoCore?scene=redirectListPage HTTP参数键值对 parameterMap url中queryString在服务端最终会转化为参数键值对。 请求体格式 请求体格式采用GraphQL协议。请求体格式分为API请求和上下文变量。以商品的test接口为例,请求格式如下。 API请求格式 query{ petShopProxyQuery { queryPage(page: {currentPage: 1, size: 1}, queryWrapper: {rsql: "(1==1)"}) { content { income id code creater { id nickname } relatedShopName shopName petTalents { id name } items { id itemName } } size totalPages totalElements } } } 图4-1-15-2 API请求格式 上下文变量 variables 请求策略requestStrategy 名称 类型 说明 checkStrategy CheckStrategyEnum 校验策略:RETURN_WHEN_COMPLETED -?全部校验完成再返回结果RETURN_WHEN_ERROR -?校验错误即返回结果 msgLevel InformationLevelEnum 消息级别:DEBUG("debug", "调试", "调试"),INFO("info", "信息", "信息"),WARN("warn", "警告", "警告"),SUCCESS("success", "成功", "成功"),ERROR("error", "错误", "错误")不设置,则只返回错误消息;上方消息级别清单,越往下级别越高。只有消息的级别高于或等于该设定级别才返回,否则会被过滤。 onlyValidate Boolean 只校验不提交数据 表4-1-15-1 请求策略requestStrategy 上下文变量式例如下。 { "requestStrategy": { "checkStrategy": "RETURN_WHEN_COMPLETED", "msgLevel":"INFO" } } 图4-1-15-3 上下文变量式例 响应体格式 协议响应内容包括data、extensions和errors三部分,extensions和errors是可缺省的。data部分为业务数据返回值。应用业务层可以在extensions中添加API返回值之外的扩展信息。extensions中包含success、messages和extra三部分,success标识请求是否成功。如果业务正确处理并返回,则errors部分为空;如果业务处理返回失败,则将错误信息添加到errors中。 正确响应格式示例如下。 { "data": { "petShopProxyQuery": { "queryPage": { "content": [ { "id": "246675081504233477", "creater": { "id": "10001" }, "relatedShopName": "oinone宠物店铺001", "shopName": "oinone宠物店铺001", "petTalents": […

    Oinone 7天入门到精通 2024年5月23日
    00
  • 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日
    00
  • 6.4 国际化之多语言

    多语言是国际化中大家最常面对的问题,我们需要对应用的页面结构元素进行翻译,也需要对系统内容进行翻译比如:菜单、数据字典等,甚至还会业务数据进行翻译。但不管什么翻译需求,我们在实现上基本可以归类为前端翻译和后端翻译。前端翻译顾名思义是在前端根据用户选择语言对内容进行翻译,反之就是后端翻译。本文会带着大家了解oinone的前端翻译与后端翻译 准备工作 pamirs-demo-boot的pom文件中引入pamirs-translate包依赖 <dependency> <groupId>pro.shushi.pamirs.core</groupId> <artifactId>pamirs-translate</artifactId> </dependency> pamirs-demo-boot的application-dev.yml文件中增加配置pamirs.boot.modules增加translate,即在启动模块中增加translate模块 pamirs: boot: modules: – translate 后端翻译(使用) 这里通过对菜单的翻译来带大家了解翻译模块 Step1 新增翻译记录 切换应用到translate模块,点击新增翻译。 选择新增翻译生效模块 选择翻译的模型为:菜单模型 源语言选择中文,目标选择English 添加翻译项目: 源术语为:商店 翻译值为:shop 状态为:激活 Step2 查看效果 应用切换到Demo模块,在右上角切换语言至英语 后端翻译(自定义模型的翻译) 在前面菜单的翻译中,似乎我们什么都没做就可以正常通过翻译模块完成多语言的切换了。是不是真如我们想象的一样,当然不是。是因为Menu模型的displayName字段加上@Field(translate = true)注解。 Step1 为PetType模型的name字段增加翻译注解 package pro.shushi.pamirs.demo.api.model; import pro.shushi.pamirs.meta.annotation.Field; import pro.shushi.pamirs.meta.annotation.Model; import pro.shushi.pamirs.meta.base.IdModel; @Model.MultiTable(typeField = "kind") @Model.model(PetType.MODEL_MODEL) @Model(displayName="品种",labelFields = {"name"}) public class PetType extends IdModel { public static final String MODEL_MODEL="demo.PetType"; @Field(displayName = "品种名" , translate = true) private String name; @Field(displayName = "宠物分类") private String kind; } Step2 重启应用查看效果 切换应用到translate模块,点击新增翻译 切换应用到Demo模块,切换中英文,查看效果 前端翻译 还记得我们前端第一个自定义动作吗?会弹出“oinone第一个自定义Action,啥也没干”,我们要对它进行翻译。 Step1 修改前端DoNothingActionWidget.ts import translateValueByKey 提示语用translateValueByKey加上翻译 const confirmRs = executeConfirm(translateValueByKey(\’oinone第一个自定义Action,啥也没干\’)||\’oinone第一个自定义Action,啥也没干\’); 前端更多翻译工具请见前端高级特性-框架之翻译工具 import { Action, ActionContextType, ActionWidget, executeConfirm, IClientAction, SPI, ViewType, Widget, translateValueByKey } from '@kunlun/dependencies'; @SPI.ClassFactory(ActionWidget.Token({ name: 'demo.doNothing' })) export class DoNothingActionWidget extends ActionWidget { @Widget.Method() public async clickAction() { const confirmRs = executeConfirm(translateValueByKey('oinone第一个自定义Action,啥也没干')||'oinone第一个自定义Action,啥也没干'); } } //定义动作元数据 Action.registerAction('*', { displayName: '啥也没干', name: 'demo.doNothing', id: 'demo.doNothing', contextType: ActionContextType.ContextFree, bindingType: [ViewType.Table] } as IClientAction); Step2 新增翻译记录 前端翻译的翻译记录对应的模型可以随意找一个放。但要注意几点: 不要找有字读配置translate = true的模型,因为会影响后端翻译性能。 最好统一到一个模型中,便于后续管理。这里大家可以自定义一个无有业务访问且本身无需要翻译的模型来挂载,避免性能损失 Step3 刷新远程资源生成前端语言文件 Step4 新增或修改.env 前端在项目根目录下新增或修改.env,可以参考.env.example文件。通过.env文件为前端配置oss文件路径,针对I18N_OSS_URL配置项。真实前端访问翻译语言文件的路径规则为:http://bucket.downloadUrl/mainDir/租户/translate/模块/语言文件。 yaml文件中oss配置的文件路径:http://pamirs.oss-cn-hangzhou.aliyuncs.com/upload/demo/ 租户/translate/模块/语言文件 前端会自动根据上下文组织 # 后端api配置 # API_BASE_URL=http://127.0.0.1:8090 # 下面是国际化语言的cdn配置,默认用当前请求链接下的路径: /pamirs/translate/${module}/i18n_${lang}.js I18N_OSS_URL=http://pamirs.oss-cn-hangzhou.aliyuncs.com/upload/demo Step5 重启前端应用看效果 对语言进行中英文切换,进入宠狗达人页面,点击【第一个自定义Action】,查看前端翻译效果

    2024年5月23日
    20
  • 4.2.7 框架之翻译工具

    一、说明 Oinone目前的默认文案是中文,如果需要使用其他语言,Oinone也提供一系列的翻译能力。 二、定义语言文件 在src/local下新增一个名为zh_cn.ts文件: 图4-2-7-1 语言文件 编辑zh_cn.ts文件,增加以下内容: const zhCN = { demo: { test: '这是测试' } } export default zhCN 图4-2-7-2 语言内容格式示意 在mian.ts注册语言资源: import { LanguageType, registryLanguage} from '@kunlun/dependencies'; import Zh_cn from './local/zh_cn' registryLanguage(LanguageType['zh-CN'], Zh_cn); 图4-2-7-3 注册语言 三、Vue模板使用 <template> <div class="petFormWrapper"> <form :model="formState" @finish="onFinish"> <a-form-item :label="translate('demo.test')" id="name" name="kind" :rules="[{ required: true, message: '请输入品种种类!', trigger: 'focus' }]"> <a-input v-model:value="formState.kind" @input="(e) => onNameChange(e, 'kind')" /> <span style="color: red">{{ getServiceError('kind') }}</span> </a-form-item> <a-form-item label="品种名" id="name" name="name" :rules="[{ required: true, message: '请输入品种名!', trigger: 'focus' }]"> <a-input v-model:value="formState.name" @input="(e) => onNameChange(e, 'name')" /> <span style="color: red">{{ getServiceError('name') }}</span> </a-form-item> </form> </div> </template> <script lang="ts"> import { defineComponent, reactive } from 'vue'; import { Form } from 'ant-design-vue'; export default defineComponent({ // 引入translate props: ['onChange', 'reloadData', 'serviceErrors', 'translate'], components: { Form }, setup(props) { const formState = reactive({ kind: '', name: '', }); const onFinish = () => { console.log(formState); }; const onNameChange = (event, name) => { props.onChange(name, event.target.value); }; const reloadData = async () => { await props.reloadData(); }; const getServiceError = (name: string) => { const error = props.serviceErrors.find(error => error.name === name);…

    2024年5月23日
    00

Leave a Reply

登录后才能评论