有关系与引用类型才让oinone具备完整的描述模型与模型间关系的能力
在PetShop以及其代理模型中已经上用到了O2M、M2O字段,分别如petItems(PetItem)和create(PamrisUser)字段,但是没有过多的讲解。本文重点举例RELATED、M2M、O2M,至于M2O留给大家自行尝试。
一、引用类型(举例)
业务类型 | Java类型 | 数据库类型 | 规则说明 |
---|---|---|---|
RELATED | 基本类型或关系类型 | 不存储或varchar、text | 引用字段【数据库规则】:点表达式最后一级对应的字段类型;数据库字段值默认为Java字段的序列化值,默认使用JSON序列化【前端交互规则】:点表达式最后一级对应的字段控件类型 |
Step1 修改PetShopProxy类
-
为PetShopProxy类新增一个引用字段relatedShopName,并加上@Field.Related("shopName")注解
-
为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;
}
Step2 重启系统查看效果
我们发现商店管理-列表页面多出了两个有值字段:引用字段shopName和引用创建者Id
图3-3-9-2 商店管理-列表页面新增两个有值字段
二、关系类型
业务类型 | Java类型 | 数据库类型 | 规则说明 |
---|---|---|---|
O2O | 模型/DataMap | 不存储或varchar、text | 一对一关系 |
M2O | 模型/DataMap | 不存储或varchar、text | 多对一关系 |
O2M | List<模型/DataMap> | 不存储或varchar、text | 一对多关系 |
M2M | List<模型/DataMap> | 不存储或varchar、text | 多对多关系 |
多值字段或者关系字段需要存储,默认使用JSON格式序列化。多值字段数据库字段类型默认为varchar(1024);关系字段数据库字段类型默认为text。
关系字段
关联关系用于描述模型间的关联方式:
- 多对一关系,主要用于明确从属关系
- 一对多关系,主要用于明确从属关系
- 多对多关系,主要用于弱依赖关系的处理,提供中间模型进行关联关系的操作
- 一对一关系,主要用于多表继承和行内合并数据
名词解释
关联关系比较重要的名词解释如下:
- 关联关系:使用relation表示,模型间的关联方式的一种描述,包括关联关系类型、关联关系双边的模型和关联关系的读写
- 关联关系字段:业务类型ttype为O2O、O2M、M2O或M2M的字段
- 关联模型:使用references表示,自身模型关联的模型
- 关联字段:使用referenceFields表示,关联模型的字段,表示关联模型的哪些字段与自身模型的哪些字段建立关系
- 关系模型:自身模型
- 关系字段:使用relationFields表示,自身模型的字段,表示自身模型的哪些字段与关联模型的哪些字段建立关系
- 中间模型,使用through表示,只有多对多存在中间模型,模型的relationship=true
举例M2M关系类型
多对多关系,主要用于弱依赖关系的处理,提供中间模型进行关联关系的操作。这也是在业务开发中很常见用于描述单据间关系,该例子会举例两种方式描述多对多关系中间表,一是中间表没有在系统显示定义模型,二种是中间表显示定义模型。第一种往往仅是维护多对多关系,第二种往往用于多对多关系中间表自身也需要管理有业务含义,中间表模型还经常额外增加其他字段。
一是中间表没有在系统显示定义模型:如果出现跨模块的场景,在分布式环境下两个模块独立启动,有可能会导致系统关系表被删除的情况发生,因为没有显示定义中间表模型,中间表的模型所属模块会根据两边模型的名称计算,如果刚好被计算到非关系字段所属模型的模块。那么单独启动非关系字段所属模型的模块,则会导致删除关系表。
为什么不直接把中间表的模型所属模块设置为关系字段所属模型的模块?因为如果这样做,当模型两边都定义了多对多关系字段则会导致M2M关系表的所属模块出现混乱。
所以这里建议大家都选用:第二种中间表显示定义模型,不论扩展性还是适应性都会好很多。请用:through=XXXRelationModel.MODEL_MODEL 或者 throughClass=XXXRelationModel.class
Step1 新建宠物达人模型,并分别为宠物商品和宠物商店增加
到宠物达人模型的字段
- 新建宠物达人模型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;
}
- 修改宠物商品模型,新增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;
}
-
修改宠物商店模型
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;
}
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;
}
Step2 重启看效果
- 进入宠物商品-编辑页面,可以看到多了推荐达人字段,点击添加选择达人记录。至于达人数据管理这里就不赘述了,只要为达人模型配一个菜单入口进去新增就好了
- 进入宠物商品-列表页面,就可以看到达人字段所选的记录
-
宠物商店的效果参考宠狗商品的编辑路径
-
查看数据库表,对应的表和字段都已经生成了
举例O2M
O2M跟M2O和M2M不一样,O2M的交互方式新建关联模型记录并一把提交,而M2O和M2M而是通过添加方式选择已有关联模型记录。
Step1 把PetShopProxyA的items字段提取到父类PetShop中去
为什么要把PetShopProxyA的items提取到PetShop中去呢?因为代理模型新增的字段都是非存储字段,如果需要保存需要自己覆盖模型的create和update方法手工保存关联对象。这个在3.3.2【模型的类型】一文中关于代理模型的内容中有详细介绍,大家忘了可以去温习下。
- 去除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";
}
- 在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;
}
Step2 重启应用查看效果
前文中PetShopProxyB代理模型扩展了catItems萌猫商品列表字段,因为该字段是代理模型的非存储字段。但上一步操作中原本PetShopProxyB从PetShopProxyA继承的非存储字段items,转而变成了从PetShop继承的存储字段。那么我们试下catItems和items都添加记录的效果吧。
- 点击菜单商店管理B进入PetShopProxyB代理模型的管理页,选择商店记录点编辑进入商店管理B-编辑页,商品列表字段处新增两个商品分别是萌猫商品1005,宠狗商品1006;
- 萌猫商品列表处字段新增商品“代理模型萌猫商品1007”
- 保存查看商品管理页面发现商品只是创建了萌猫商品1005和宠狗商品1006,而没有代理模型萌猫商品
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低代码应用平台体验