3.3.9 字段类型之关系与引用

有关系与引用类型才让oinone具备完整的描述模型与模型间关系的能力

在PetShop以及其代理模型中已经上用到了O2M、M2O字段,分别如petItems(PetItem)和create(PamrisUser)字段,但是没有过多的讲解。本文重点举例RELATED、M2M、O2M,至于M2O留给大家自行尝试。

一、引用类型(举例)

业务类型 Java类型 数据库类型 规则说明
RELATED 基本类型或关系类型 不存储或varchar、text 引用字段【数据库规则】:点表达式最后一级对应的字段类型;数据库字段值默认为Java字段的序列化值,默认使用JSON序列化【前端交互规则】:点表达式最后一级对应的字段控件类型

表3-3-9-1 字段引用类型

Step1 修改PetShopProxy类

  1. 为PetShopProxy类新增一个引用字段relatedShopName,并加上@Field.Related("shopName")注解

  2. 为PetShopProxy类新增一个引用字段createrId,并加上@Field.Related({"creater","id"})注解

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

import pro.shushi.pamirs.demo.api.model.PetShop;
import pro.shushi.pamirs.meta.annotation.Field;
import pro.shushi.pamirs.meta.annotation.Model;
import pro.shushi.pamirs.meta.enmu.ModelTypeEnum;
import pro.shushi.pamirs.user.api.model.PamirsUser;

@Model.model(PetShopProxy.MODEL_MODEL)
@Model.Advanced(type = ModelTypeEnum.PROXY)
@Model(displayName = "宠物店铺代理模型",summary="宠物店铺代理模型")
public class PetShopProxy extends PetShop {

    public static final String MODEL_MODEL="demo.PetShopProxy";

    @Field.many2one
    @Field(displayName = "创建者",required = true)
    @Field.Relation(relationFields = {"createUid"},referenceFields = {"id"})
    private PamirsUser creater;

    @Field.Related("shopName")
    @Field(displayName = "引用字段shopName")
    private String relatedShopName;

    @Field.Related({"creater","id"})
    @Field(displayName = "引用创建者Id")
    private String createrId;

}

图3-3-9-1 修改PetShopProxy类

Step2 重启系统查看效果

我们发现商店管理-列表页面多出了两个有值字段:引用字段shopName和引用创建者Id

1711608487971-8e48aebc-e6c3-4143-a95d-ca649dfdee8f图3-3-9-2 商店管理-列表页面新增两个有值字段

二、关系类型

业务类型 Java类型 数据库类型 规则说明
O2O 模型/DataMap 不存储或varchar、text 一对一关系
M2O 模型/DataMap 不存储或varchar、text 多对一关系
O2M List<模型/DataMap> 不存储或varchar、text 一对多关系
M2M List<模型/DataMap> 不存储或varchar、text 多对多关系

表3-3-9-2 字段关系类型

多值字段或者关系字段需要存储,默认使用JSON格式序列化。多值字段数据库字段类型默认为varchar(1024);关系字段数据库字段类型默认为text。

关系字段

关联关系用于描述模型间的关联方式:

  • 多对一关系,主要用于明确从属关系
  • 一对多关系,主要用于明确从属关系
  • 多对多关系,主要用于弱依赖关系的处理,提供中间模型进行关联关系的操作
  • 一对一关系,主要用于多表继承和行内合并数据

3.3.9 字段类型之关系与引用

图3-3-9-3 字段关联关系

名词解释

关联关系比较重要的名词解释如下:

  • 关联关系:使用relation表示,模型间的关联方式的一种描述,包括关联关系类型、关联关系双边的模型和关联关系的读写
  • 关联关系字段:业务类型ttype为O2O、O2M、M2O或M2M的字段
  • 关联模型:使用references表示,自身模型关联的模型
  • 关联字段:使用referenceFields表示,关联模型的字段,表示关联模型的哪些字段与自身模型的哪些字段建立关系
  • 关系模型:自身模型
  • 关系字段:使用relationFields表示,自身模型的字段,表示自身模型的哪些字段与关联模型的哪些字段建立关系
  • 中间模型,使用through表示,只有多对多存在中间模型,模型的relationship=true

举例M2M关系类型

多对多关系,主要用于弱依赖关系的处理,提供中间模型进行关联关系的操作。这也是在业务开发中很常见用于描述单据间关系,该例子会举例两种方式描述多对多关系中间表,一是中间表没有在系统显示定义模型,二种是中间表显示定义模型。第一种往往仅是维护多对多关系,第二种往往用于多对多关系中间表自身也需要管理有业务含义,中间表模型还经常额外增加其他字段。

一是中间表没有在系统显示定义模型:如果出现跨模块的场景,在分布式环境下两个模块独立启动,有可能会导致系统关系表被删除的情况发生,因为没有显示定义中间表模型,中间表的模型所属模块会根据两边模型的名称计算,如果刚好被计算到非关系字段所属模型的模块。那么单独启动非关系字段所属模型的模块,则会导致删除关系表。

为什么不直接把中间表的模型所属模块设置为关系字段所属模型的模块?因为如果这样做,当模型两边都定义了多对多关系字段则会导致M2M关系表的所属模块出现混乱。

所以这里建议大家都选用:第二种中间表显示定义模型,不论扩展性还是适应性都会好很多。请用:through=XXXRelationModel.MODEL_MODEL 或者 throughClass=XXXRelationModel.class

Step1 新建宠物达人模型,并分别为宠物商品和宠物商店增加

到宠物达人模型的字段

  1. 新建宠物达人模型PetTalent
package pro.shushi.pamirs.demo.api.model;

import pro.shushi.pamirs.meta.annotation.Field;
import pro.shushi.pamirs.meta.annotation.Model;

@Model.model(PetTalent.MODEL_MODEL)
@Model(displayName = "宠物达人",summary="宠物达人",labelFields ={"name"})
public class PetTalent extends AbstractDemoIdModel{
    public static final String MODEL_MODEL="demo.PetTalent";

    @Field(displayName = "达人")
    private String name;

}

图3-3-9-4 新建宠物达人模型PetTalent

  1. 修改宠物商品模型,新增many2many字段petTalents,类型为List ,并加上注解@Field.many2many(relationFields = {"petItemId"},referenceFields = {"petTalentId"},through = PetItemRelPetTalent.MODEL_MODEL),through为指定关联中间表。
package pro.shushi.pamirs.demo.api.model;

import pro.shushi.pamirs.demo.api.tmodel.PetItemDetail;
import pro.shushi.pamirs.meta.annotation.Field;
import pro.shushi.pamirs.meta.annotation.Model;
import pro.shushi.pamirs.meta.enmu.NullableBoolEnum;

import java.math.BigDecimal;
import java.util.List;

@Model.model(PetItem.MODEL_MODEL)
@Model(displayName = "宠物商品",summary="宠物商品",labelFields = {"itemName"})
public class PetItem  extends AbstractDemoCodeModel{

    public static final String MODEL_MODEL="demo.PetItem";

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

    @Field(displayName = "商品价格",required = true)
    private BigDecimal price;

    @Field(displayName = "店铺",required = true)
    @Field.Relation(relationFields = {"shopId"},referenceFields = {"id"})
    private PetShop shop;

    @Field(displayName = "店铺Id",invisible = true)
    private Long shopId;

    @Field(displayName = "品种")
    @Field.many2one
    @Field.Relation(relationFields = {"typeId"},referenceFields = {"id"})
    private PetType type;

    @Field(displayName = "品种类型",invisible = true)
    private Long typeId;

    @Field(displayName = "详情", serialize = Field.serialize.JSON, store = NullableBoolEnum.TRUE)
    @Field.Advanced(columnDefinition = "varchar(1024)")
    private List<PetItemDetail> petItemDetails;

    @Field(displayName = "商品标签",serialize = Field.serialize.COMMA,store = NullableBoolEnum.TRUE,multi = true)
    @Field.Advanced(columnDefinition = "varchar(1024)")
    private List<String> tags;

    @Field.many2many(relationFields = {"petItemId"},referenceFields = {"petTalentId"},through = PetItemRelPetTalent.MODEL_MODEL)
    @Field(displayName = "推荐达人",summary = "推荐该商品的达人们")
    private List<PetTalent> petTalents;

}

图3-3-9-5 修改宠物商品模型

  1. 修改宠物商店模型

    a. 新增many2many关联模型PetShopRelPetTalent并继承BaseRelation,BaseRelation为关系模型抽象基类用于承载多对多关系,是多对多关系的中间模型,数据模型主键可以不是ID。更多类似抽象基类详见3.3.2【模型的类型】一文中关于模型快捷继承相关内容。

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.BaseRelation;

@Model.model(PetShopRelPetTalent.MODEL_MODEL)
@Model(displayName = "宠物店铺与达人关联表",summary="宠物店铺与达人关联表")
public class PetShopRelPetTalent extends BaseRelation {

    public static final String MODEL_MODEL="demo.PetShopRelPetTalent";
    @Field(displayName = "店铺Id")
    private Long petShopId;

    @Field(displayName = "达人Id")
    private Long petTalentId;

}

图3-3-9-6 新增many2many关联模型PetShopRelPetTalent

b. 修改宠物商店模型,新增many2many字段petTalents,类型为List ,并加上注解@Field.many2many(relationFields = {"petShopId"},referenceFields = {"petTalentId"},throughClass =PetShopRelPetTalent.class) ,throughClass为指定关联模型类

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

import pro.shushi.pamirs.core.common.enmu.DataStatusEnum;
import pro.shushi.pamirs.demo.api.enumeration.PetShopOptionEnum;
import pro.shushi.pamirs.meta.annotation.Field;
import pro.shushi.pamirs.meta.annotation.Model;
import pro.shushi.pamirs.meta.enmu.DateFormatEnum;
import pro.shushi.pamirs.meta.enmu.DateTypeEnum;

import java.math.BigDecimal;
import java.sql.Time;
import java.util.Date;
import java.util.List;

@Model.model(PetShop.MODEL_MODEL)
@Model(displayName = "宠物店铺",summary="宠物店铺",labelFields ={"shopName"} )
@Model.Code(sequence = "DATE_ORDERLY_SEQ",prefix = "P",size=6,step=1,initial = 10000,format = "yyyyMMdd")
public class PetShop extends AbstractDemoIdModel {
    public static final String MODEL_MODEL="demo.PetShop";

    @Field(displayName = "店铺编码")
    private String code;

    @Field(displayName = "店铺名称",required = true,immutable=true)
    private String shopName;

    @Field(displayName = "开店时间",required = true)
    @Field.Advanced(readonly = true)
    private Time openTime;

    @Field(displayName = "闭店时间",required = true)
    private Time closeTime;

    @Field.Enum
    @Field(displayName = "数据状态",defaultValue = "DISABLED",required = true,summary = "枚举可选项举例")
    private DataStatusEnum dataStatus;

    @Field(displayName = "店铺标志")
    private List<PetShopOptionEnum> options;

    @Field(displayName = "一年内新店")
    private Boolean oneYear;

    @Field.Float
    @Field(displayName = "店内员工平均年龄")
    private BigDecimal averageAge;

    @Field.Text
    @Field(displayName = "描述")
    private String description;

    @Field.Html
    @Field(displayName = "html描述")
    private String descHtml;

    @Field.Date(type = DateTypeEnum.DATE,format = DateFormatEnum.DATE)
    @Field(displayName = "店庆")
    private Date anniversary;

//    前端未提供默认支持,演示不了。这个留在我们自定义前端页面时演示
//    @Field.Date(type = DateTypeEnum.YEAR,format = DateFormatEnum.YEAR)
//    @Field(displayName = "开店年份")
//    private Date shopYear;

    @Field.Money
    @Field(displayName = "收入",priority = 1)
    private BigDecimal income;

    @Field.many2many(relationFields = {"petShopId"},referenceFields = {"petTalentId"},throughClass =PetShopRelPetTalent.class)
    @Field(displayName = "推荐达人",summary = "推荐该商品的达人们")
    private List<PetTalent> petTalents;

}

图3-3-9-7 新增many2many字段petTalents

Step2 重启看效果

  1. 进入宠物商品-编辑页面,可以看到多了推荐达人字段,点击添加选择达人记录。至于达人数据管理这里就不赘述了,只要为达人模型配一个菜单入口进去新增就好了

3.3.9 字段类型之关系与引用

图3-3-9-8 新增many2many字段petTalents

  1. 进入宠物商品-列表页面,就可以看到达人字段所选的记录

3.3.9 字段类型之关系与引用

图3-3-9-9 进入宠物商品-列表页面

  1. 宠物商店的效果参考宠狗商品的编辑路径

  2. 查看数据库表,对应的表和字段都已经生成了

3.3.9 字段类型之关系与引用

图3-3-9-10 数据库表对应的表和字段都已生成

举例O2M

O2M跟M2O和M2M不一样,O2M的交互方式新建关联模型记录并一把提交,而M2O和M2M而是通过添加方式选择已有关联模型记录。

Step1 把PetShopProxyA的items字段提取到父类PetShop中去

为什么要把PetShopProxyA的items提取到PetShop中去呢?因为代理模型新增的字段都是非存储字段,如果需要保存需要自己覆盖模型的create和update方法手工保存关联对象。这个在3.3.2【模型的类型】一文中关于代理模型的内容中有详细介绍,大家忘了可以去温习下。

  1. 去除PetShopProxyA的items字段
package pro.shushi.pamirs.demo.api.proxy;

import pro.shushi.pamirs.demo.api.model.PetShop;
import pro.shushi.pamirs.meta.annotation.Model;
import pro.shushi.pamirs.meta.enmu.ModelTypeEnum;

@Model.model(PetShopProxyA.MODEL_MODEL)
@Model.Advanced(type = ModelTypeEnum.PROXY)
@Model(displayName = "宠物店铺代理模型A",summary="宠物店铺代理模型A")
public class PetShopProxyA extends PetShop {
    public static final String MODEL_MODEL="demo.PetShopProxyA";

}

图3-3-9-11 去除PetShopProxyA的items字段

  1. 在PetShop中增加items字段
package pro.shushi.pamirs.demo.api.model;

import pro.shushi.pamirs.core.common.enmu.DataStatusEnum;
import pro.shushi.pamirs.demo.api.enumeration.PetShopOptionEnum;
import pro.shushi.pamirs.meta.annotation.Field;
import pro.shushi.pamirs.meta.annotation.Model;
import pro.shushi.pamirs.meta.enmu.DateFormatEnum;
import pro.shushi.pamirs.meta.enmu.DateTypeEnum;

import java.math.BigDecimal;
import java.sql.Time;
import java.util.Date;
import java.util.List;

@Model.model(PetShop.MODEL_MODEL)
    @Model(displayName = "宠物店铺",summary="宠物店铺",labelFields ={"shopName"} )
    @Model.Code(sequence = "DATE_ORDERLY_SEQ",prefix = "P",size=6,step=1,initial = 10000,format = "yyyyMMdd")
    public class PetShop extends AbstractDemoIdModel {
        public static final String MODEL_MODEL="demo.PetShop";

        @Field(displayName = "店铺编码")
        private String code;

        @Field(displayName = "店铺编码2")
        @Field.Sequence(sequence = "DATE_ORDERLY_SEQ",prefix = "C",size=6,step=1,initial = 10000,format = "yyyyMMdd")
        private String codeTwo;

        @Field(displayName = "店铺名称",required = true,immutable=true)
        private String shopName;

        @Field(displayName = "开店时间",required = true)
        private Time openTime;

        @Field(displayName = "闭店时间",required = true)
        private Time closeTime;

        @Field.Enum
        @Field(displayName = "数据状态",defaultValue = "DRAFT",required = true,summary = "枚举可选项举例")
        private DataStatusEnum dataStatus;

        @Field(displayName = "店铺标志")
        private List<PetShopOptionEnum> options;

        @Field(displayName = "一年内新店")
        private Boolean oneYear;

        @Field.Float
        @Field(displayName = "店内员工平均年龄")
        private BigDecimal averageAge;

        @Field.Text
        @Field(displayName = "描述")
        private String description;

        @Field.Html
        @Field(displayName = "html描述")
        private String descHtml;

        @Field.Date(type = DateTypeEnum.DATE,format = DateFormatEnum.DATE)
        @Field(displayName = "店庆")
        private Date anniversary;

        @Field.Date(type = DateTypeEnum.YEAR,format = DateFormatEnum.YEAR)
        @Field(displayName = "开店年份")
        private Date publishYear;

        @Field.Money
        @Field(displayName = "收入",priority = 1)
        private BigDecimal income;

        @Field.many2many(relationFields = {"petShopId"},referenceFields = {"petTalentId"},throughClass =PetShopRelPetTalent.class)
        @Field(displayName = "推荐达人",summary = "推荐该商品的达人们")
        private List<PetTalent> petTalents;

        @Field.one2many
        @Field(displayName = "商品列表")
        @Field.Relation(relationFields = {"id"},referenceFields = {"shopId"})
        private List<PetItem> items;

    }

图3-3-9-12 在PetShop中增加items字段

Step2 重启应用查看效果

前文中PetShopProxyB代理模型扩展了catItems萌猫商品列表字段,因为该字段是代理模型的非存储字段。但上一步操作中原本PetShopProxyB从PetShopProxyA继承的非存储字段items,转而变成了从PetShop继承的存储字段。那么我们试下catItems和items都添加记录的效果吧。

  1. 点击菜单商店管理B进入PetShopProxyB代理模型的管理页,选择商店记录点编辑进入商店管理B-编辑页,商品列表字段处新增两个商品分别是萌猫商品1005,宠狗商品1006;

3.3.9 字段类型之关系与引用

图3-3-9-13 示例效果图

3.3.9 字段类型之关系与引用

图3-3-9-14 示例效果图

  1. 萌猫商品列表处字段新增商品“代理模型萌猫商品1007”

3.3.9 字段类型之关系与引用

图3-3-9-15 示例效果图

3.3.9 字段类型之关系与引用

图3-3-9-16 示例效果图

  1. 保存查看商品管理页面发现商品只是创建了萌猫商品1005和宠狗商品1006,而没有代理模型萌猫商品

3.3.9 字段类型之关系与引用

图3-3-9-17 示例效果图

O2O

O2O与M2O相比,在从前端交互体验上跟M2O一样都是下拉单选,但有一个区别在于目标对象只能被一个操作对象绑定。

限制:同时模型的O2O字段关联的模型与本模型必须要在同一模块中,否则会报错。因为O2O字段需要两边对等建立关系字段

O2M,O2O字段的提交策略(该版本还未支持)

注解的onUpdate和onDelete属性指定在删除模型或更新模型关系字段值时,对关联模型进行的相应操作。操作包括RESTRICT、NO ACTION、SET NULL和CASCADE,默认值为SET NULL。

  • RESTRICT是指模型与关联模型有关联记录的情况下,引擎会阻止模型关系字段的更新或删除模型记录;

  • NO ACTION是指不作约束(这里与数据库约束的定义不相同);

  • CASCADE表示在更新模型关系字段或者删除模型时,级联更新关联模型对应记录的关联字段值或者级联删除关联模型对应记录;

  • SET NULL则是表示在更新模型关系字段或者删除模型的时候,关联模型的对应关联字段将被SET NULL(该字段值允许为null的情况下,若不允许为null,则引擎阻止对模型的操作)。

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

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

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

相关推荐

  • 4.1.11 函数之异步执行

    异步任务是非常常见的一种开发模式,它在分布式的开发模式中有很多应用场景如: 高并发场景中,我们一般采用把长流程切短,用异步方式去掉可以异步的非关键功能,缩小主流程响应时间,提升用户体验 异构系统的集成调用,通过异步任务完成解耦与自动重试 分布式系统最终一致性的可选方案 今天我们了解oinone是如何结合Spring+TbSchedule来完成异步任务 一、TbSchedule介绍 它是一个支持分布式的调度框架,让批量任务或者不断变化的任务能够被动态的分配到多个主机的JVM中,在不同的线程组中并行执行,所有的任务能够被不重复,不遗漏的快速处理。基于ZooKeeper的纯Java实现,由Alibaba开源。在互联网和电商领域TBSchedule的使用非常广泛,目前被应用于阿里巴巴、淘宝、支付宝、京东、聚美、汽车之家、国美等很多互联网企业的流程调度系统。也是笔者早期在阿里参与设计的一款产品。 oinone的异步任务执行原理(如下图4-1-11-1所示),先做一个大致了解: 图4-1-11-1 Oinone的异步任务执行原理图 基础管理工具 下载tbSchedule的控制台jar包去除文件后缀.txt(详见本书籍【附件一】)pamirs-middleware-schedule-console-3.0.1.jar.txt(31.2 MB) 启动控制台 java -jar pamirs-middleware-schedule-console-3.0.1.jar 图4-1-11-2 控制台启动方式 访问地址 http://127.0.0.1:10014/schedule/index.jsp?manager=true 图4-1-11-3 访问地址 配置zk连接参数 图4-1-11-4 配置zk连接参数 oinone默认实现任务类型 图4-1-11-5 Oinone默认实现任务类型 baseScheduleNoTransactionTask baseScheduleTask remoteScheduleTask — 适用于pamirs-middleware-schedule独立部署场景 serialBaseScheduleNoTransactionTask serialBaseScheduleTask serialRemoteScheduleTask — 适用于pamirs-middleware-schedule独立部署场景 cycleScheduleNoTransactionTask delayMsgTransferScheduleTask deleteTransferScheduleTask 注: a. 默认情况下:所有任务的任务项都只配置了一个任务项0,只有一台机器能分配任务。 1. 如果要修改配置可以在启动项目中放置schedule.json,来修改配置 2. 人工进入控制修改任务对应任务项的配置 b. 如果想为某一个核心任务配置的独立调度器,不受其他任务执行影响。那么见独立调度的异步任务 任务表相关说明 图4-1-11-6 任务表相关说明 二、构建第一个异步任务(举例) Step1 新建PetShopService和PetShopServiceImpl 1 新建PetShopService定义updatePetShops方法 package pro.shushi.pamirs.demo.api.service; import pro.shushi.pamirs.demo.api.model.PetShop; import pro.shushi.pamirs.meta.annotation.Fun; import pro.shushi.pamirs.meta.annotation.Function; import java.util.List; @Fun(PetShopService.FUN_NAMESPACE) public interface PetShopService { String FUN_NAMESPACE = "demo.PetShop.PetShopService"; @Function void updatePetShops(List<PetShop> petShops); } 图4-1-11-7 新建PetShopService定义updatePetShops方法 PetShopServiceImpl实现PetShopService接口并在updatePetShops增加@XAsync注解 displayName = "异步批量更新宠物商店",定义异步任务展示名称 limitRetryNumber = 3,定义任务失败重试次数,,默认:-1不断重试 nextRetryTimeValue = 60,定义任务失败重试的时间数,默认:3 nextRetryTimeUnit,定义任务失败重试的时间单位,默认:TimeUnitEnum.SECOND delayTime,定义任务延迟执行的时间数,默认:0 delayTimeUnit,定义任务延迟执行的时间单位,默认:TimeUnitEnum.SECOND package pro.shushi.pamirs.demo.core.service; import org.springframework.stereotype.Component; import pro.shushi.pamirs.demo.api.model.PetShop; import pro.shushi.pamirs.demo.api.service.PetShopService; import pro.shushi.pamirs.meta.annotation.Fun; import pro.shushi.pamirs.meta.annotation.Function; import pro.shushi.pamirs.trigger.annotation.XAsync; import java.util.List; @Fun(PetShopService.FUN_NAMESPACE) @Component public class PetShopServiceImpl implements PetShopService { @Override @Function @XAsync(displayName = "异步批量更新宠物商店",limitRetryNumber = 3,nextRetryTimeValue = 60) public void updatePetShops(List<PetShop> petShops) { new PetShop().updateBatch(petShops); } } 图4-1-11-8 实现PetShopService接口并在updatePetShops增加@XAsync注解 Step2 修改PetShopBatchUpdateAction的conform方法 引入PetShopService 修改conform方法 利用ArgUtils进行参数转化,ArgUtils会经常用到。 调用petShopService.updatePetShops方法 package pro.shushi.pamirs.demo.core.action; …… 引依赖类 @Model.model(PetShopBatchUpdate.MODEL_MODEL) @Component public class PetShopBatchUpdateAction { @Autowired private PetShopService petShopService; ……其他代码 @Action(displayName = "确定",bindingType = ViewTypeEnum.FORM,contextType = ActionContextTypeEnum.SINGLE) public PetShopBatchUpdate conform(PetShopBatchUpdate data){ if(data.getPetShopList() == null || data.getPetShopList().size()==0){ throw…

    2024年5月23日
    1.4K00
  • 2.4.1 Oinone独特性之单体与分布式的灵活切换

    企业数字化转型需要处理分布式带来的复杂性和成本问题。尽管这些问题令人望而却步,但分布式架构对于大部分企业仍然是必须的选择。如果一个低代码平台缺乏分布式能力,那么它的性能就无法满足客户的要求。相比之下,Oinone平台通过对部署的创新(如图2-6所示),成功实现了分布式架构的支持,而且能够按照客户的业务发展需求,灵活选择不同的部署模式,同时节约企业成本,提升创新效率。这一创新是Oinone平台与其他低代码平台的重要区别,能够满足客户预期发展并兼顾成本效益。 图2-6 传统部署方式VS Oinone部署方式 实现原理 要实现灵活部署的特性,必须满足两个基本要求: 开发过程中不需要过多关注分布式技术,就像开发单体应用一样简单。代码在运行时应该能够根据模块是否在运行容器中,来决定路由走本地还是远程。这样可以大大减少研发人员的工作量和技术复杂度。 研发与部署要分离,即"开发单体应用一样开发分布式应用,而部署形式由后期决定"。为此,我们的工程结构支持多种启动模式,并逐一介绍了针对不同场景的工程结构类型(如下图2-7所示)。这样可以让客户在后期根据业务发展情况和需求,选择最适合的部署模式,从而达到灵活部署的目的。 图2-7 Oinone工程结构梳理 在整个工程结构上,我们秉承了Spring Boot的规范,不会改变大家的工程习惯。而Oinone的部署能力则可以让我们更灵活地应对各种情况。现在,我们来逐一介绍几种常规的工程结构以及它们适用的场景: 单模块工程结构(常规操作) a. 这是非常标准的Spring Boot工程,适用于简单的应用场景开发以及入门学习。 多模块工程结构(常规操作) a. 这是非常标准的多Spring Boot工程,可以实现分布式独立启动,适用于常规的分布式应用场景开发。 多模块工程结构-独立boot工程模式 a. 这种工程结构在多模块工程的基础上,通过独立的boot工程来支撑多部署方式。适用于中大型分布式应用场景开发。 b. 然而,随着工程越来越多,我们也会面临一些问题: ⅰ研发:环境准备非常困难,每个模块都要单独启动,研发调试跟踪困难。 ⅱ部署:分布式的高可靠性保证需要每个模块至少有两个部署节点,但在模块较多的情况下,起步成本非常高。同时,企业初期业务不稳定且规模较小,使用多模块工程的第二种模式会增加问题排查难度和成本。 c. 此时,Oinone的多模块工程下的独立boot工程模式部署就可以发挥其灵活性,让研发和业务起步阶段可以选择all-in-one模式,等到业务发展到一定规模的时候,只需要把线上部署模式切换成模块独立部署,而研发还可以保留all-in-one模式的优势。 d. 值得注意的是,分分合合的部署模式在传统互联网架构和低代码或无代码平台上都是有代价的,但是Oinone却可以灵活适配,只需要在boot工程的yml文件中写入需要加载的模块就可以解决。此处我们仅介绍多模块加载配置,选择性忽略其他无关配置,具体配置(如下图2-8所示)。 pamirs: boot: init: true sync: true modules: – base – resource – sequence – user – auth – web tenants: – pamirs 图2-8 Oinone yml配置图大型多场景工程结构-独立boot工程模式: a. 在多模块工程结构基础上的加强版,增加CDM层设计,让不同场景即保持数据统一,又保持逻辑独立。这种工程结构特别适用于大型企业软件开发,其中涉及到多个场景的情况,例如B端和C端的应用,或者跨不同业务线的应用,能够保证数据的一致性,同时也能够保持逻辑独立,避免不同场景间的代码冲突。 b. 这种工程结构是我们Oinone支撑“企业级软件生态”的核心,我们可以把场景A当作我们官方应用,场景B当作其他第三方伙伴应用。在这个工程结构下,我们的客户可以定制化开发自己的应用,同时我们也可以通过这种模式来支持我们的伙伴们进行开发,实现多方共赢。 c. 基于独立boot工程模式,我们同样对应多种部署模式应对不同情况,并统一管理所有伙伴应用。这种工程结构的优点是扩展性好,可以支持不同规模的应用,并且可以根据需要进行快速扩展或缩小规模,具有很高的灵活性。 基于标准产品的二开工程结构,是指基于标准产品进行二次开发,满足客户特定需求的工程结构。这种模式下,Oinone提供标准产品,客户可以根据自己的需求进行二次开发,实现定制化需求,同时可以利用我们的模块化开发特性,将每一个需求作为一个模块进行开发和管理。这种工程结构的优点是能够快速满足客户特定需求,同时也具有很好的可维护性和可扩展性,因为每个需求都是一个独立的模块,可以方便地进行维护和扩展。在下一篇“Oinone独特性之每一个需求都是一个模块”文章中有详细介绍。

    2024年5月23日
    2.3K00
  • 1.2 Oinone的致敬

    占在巨人的肩膀上,天地孤影任我行 1.2.1 数字化时代Oinone接棒Odoo 在数字化时代,中国在互联网化的应用、技术的领先毋庸置疑,但在软件的工程化、产品化输出方面仍有许多改进的空间。这时,我了解到了Odoo——一个国外非常优秀的开源ERP厂商,全球ERP用户数量排名第一,百级别员工服务全球客户。Odoo的工程化能力和商业模式深深吸引了我,它是软件行业典型的产品制胜和长期主义者的胜利之一。 在2019年,也就是数式刚成立的时候,我们跟很多投资人聊起公司的对标是谁,我不是要成为数字化时代的SAP,而是要成为Odoo。然而,当时大部分国内投资人并不了解Odoo,尽管它已经是全球最大的ERP厂商之一,因为当时Odoo还没有明确的估值。直到2021年7月份获得Summit Partners的2.15亿美元投资后,Odoo才正式成为IT独角兽企业。 Odoo对我们提供了极大的启示,因此我们致敬Odoo,同样选择开源,每年对产品进行升级发布。如今,Odoo15已经发布,而Oinone也已推出第三版,恰好相隔12年,这是一个时代的接棒,从信息化升迁至数字化。 1.2.2Oinone与Odoo的不同之处 技术方面的不同 在技术上,Oinone和Odoo有相同之处,也有不同之处。它们都基于元数据驱动的软件系统,但是它们在如何让元数据运作的机制上存在巨大差异。Odoo是企业管理场景的单体应用,而Oinone则致力于企业商业场景的云原生应用。因此,它们在技术栈的选择、前后端协议设计、架构设计等方面存在差异。 场景方面的不同 在场景上,Oinone和Odoo呈现许多差异。相对于SAP这些老牌ERP厂商,Odoo算是西方在企业级软件领域的后起之秀,其软件构建方式、开源模式和管理理念在国外取得了非凡的成就。然而,在国内,Odoo并没有那么成功或者并没有那么知名。国内做Odoo的伙伴普遍认为,Odoo与中国用户的交互风格不符,收费模式设计以及外汇管制使商业活动受到限制,本地化服务不到位,国内生态没有形成合力,伙伴们交流合作都非常少。另外,Odoo在场景方面主要围绕内部流程管理,与国内老牌ERP如用友、金蝶重叠,市场竞争激烈。相比之下,Oinone看准了企业视角由内部管理转向业务在线、生态在线(协同)带来的新变化,聚焦新场景,利用云、端等新技术的发展,从企业内外部协同入手,以业务在线驱动企业管理流程升级。它先立足于国内,做好国内生态服务,再着眼未来的国际化。 无代码设计器的定位 在无代码设计器的定位上,Odoo的无代码设计器是一个非常轻量的辅助工具,因为ERP场景下,一个企业实施完以后基本几年不会变,流程稳定度非常高。相反,Oinone为适应"企业业务在线化后,所有的业务变化与创新都需要通过系统来触达上下游,从而敏捷响应快速创新"的时代背景,重点打造出五大设计器。(如下图1-2所示)。 图1-2 Oinone五大设计器 在数字化时代中国软件将接棒世界,而Oinone也要接棒Odoo,把数字化业务与技术的最佳实践赋能给企业,帮助企业数字化转型不走弯路!

    2024年5月23日
    2.3K00
  • 5.4 基础支撑之商业关系域

    PamirsPartner作为商业关系与商业行为的主体,那么PamirsPartner间的关系如何描述,本文将介绍两种常见的设计思路,从思维和实现两方面进行对比,给出oinone为啥选择关系设计模式的原因。 一、两种设计模式对比 设计模式思路介绍 角色设计模式思路介绍 从产品角度枚举所有商业角色,每个商业角色对应一个派生的商业主体,并把主体间的关系类型进行整理。 图5-4-1 角色设计模式 关系设计模式思路介绍 从产品角度枚举所有商业角色,每个商业角色对应一个派生的主体间商业关系 图5-4-2 关系设计模式 设计模式对应实现介绍 角色设计模式实现介绍 不单商业主体需要扩展,关系也要额外维护,可以是字段或是关系表。一般M2O和O2M字段维护,M2M关系表维护。 创建合同场景中甲方选择【商业主体A】,乙方必须是【商业主体A】有关联的经销商、分销商、零售商、供应商等,则在角色设计模式下就非常麻烦,因为关系都是独立维护的 图5-4-3 角色设计模式实现介绍 关系设计模式实现介绍 只需维护商业关系扩展 同时在设计上收敛了商业关系,统一管理应对不同场景都比较从容 图5-4-4 关系设计模式实现介绍 二、oinone商业关系的默认实现 首先oinone的商业关系选择关系设计模式 其次模型上采用多表继承模式,父模型上维护核心字段,子模型维护个性化字段。

    2024年5月23日
    2.0K00

Leave a Reply

Please Login to Comment