模型的继承
模型的继承
继承的方式
继承方式可以分为五种:
- 抽象基类
ABSTRACT
- 扩展继承
EXTENDS
- 多表继承
MULTI_TABLE
- 代理继承
PROXY
- 临时继承
TRANSIENT
继承的约束
通用约束
对于扩展继承,查询的时候,父模型只能查询到父模型字段的数据,子模型可以查询出父模型及子模型的字段数据(因为派生关系所以子模型复刻了一份父模型的字段到子模型中)。
系统不会为抽象基类创建实际的数据库表,它们也没有默认的数据管理器,不能被实例化也无法直接保存,它们就是用来被继承的。抽象基类完全就是用来保存子模型们共有的内容部分,达到重用的目的。当它们被继承时,它们的字段会全部复制到子模型中。
系统不支持非jar包依赖模型的继承。
多表继承具有阻断效应,子模型无法继承多表继承父模型的存储父模型的字段,需要使用@Model.Advanced
注解的inherited
属性显示声明继承父模型的父模型。但是可以继承多表继承父模型的抽象父模型的字段。
可以使用@Model.Advanced
的unInheritedFields
和unInheritedFunctions
属性设置不从父类继承的字段和函数。
跨模块继承约束
如果模型间的继承是跨模块继承,应该与模型所属模块建立依赖关系;如果模块间有互斥关系,则不允许建立模块依赖关系,同理模型间也不允许存在继承关系。
跨模块代理继承,对代理模型的非inJvm函数调用将使用远程调用方式;跨模块扩展(同表)继承将使用本地调用方式,如果是数据管理器函数,将直连数据源。
模型类型与继承约束
- 抽象模型可继承:抽象模型(Abstract)
- 临时模型可继承:抽象模型(Abstract)、传输模型(Transient)
- 存储模型可继承:抽象模型(Abstract)、存储模型(Store)、存储模型(多表,Multi-table Store),不可继承多个Store或Multi-table Store
- 多表存储模型(父)可继承:同扩展继承
- 多表存储模型(子)在继承单个Multi-table Store后可继承:抽象模型(Abstract)、存储模型(Store),不可继承多个Store
- 代理模型可继承:
- 抽象模型(Abstract),须搭配继承Store、Multi-table Store或Proxy
- 存储模型(Store),不可继承多个Store或Multi-table Store
- 存储模型(多表,Multi-table Store),不可继承多个Store或Multi-table Store
- 代理模型(Proxy),可继承多个Proxy,但多个父Proxy须继承自同一个Store或Multi-table Store,且不能再继承其他Store或Multi-table Store
同名字段以模型自身字段为有效配置,若模型自身不存在该字段,继承字段以第一个加载的字段为有效配置,所以在多重继承的情况下,未避免继承同名父模型字段的不确定性,在自身模型配置同名字段来确定生效配置。
继承的使用场景
模型的继承可以继承父模型的元信息、字段、数据管理器和函数
- 抽象基类 解决公用字段问题
- 扩展继承 解决开放封闭原则、跨模块扩展等问题
- 多表继承 解决多型派生类字段差异问题和前端多存储模型组合外观问题
- 代理继承 解决同一模型在不同场景下的多态问题(一表多态)
- 临时继承 解决使用现有模型进行数据传输问题
举例,前端多存储模型组合外观问题可通过多表继承的子模型再一对一关联到关联模型,同时使用排除继承字段去掉不需要继承的字段。子模型通过默认模型管理器提供查询功能给前端,默认查询会查询子模型数据列表并在列表行内根据一对一关系查出关联模型数据合并,关联模型数据展现形态在行内是平铺还是折叠,在详情是分组还是选项卡可以自定义view进行配置
扩展继承 父子同表,模型在所有场景都有一致化的表现,意味着原模型被扩展成了新模型,父子模型的表名一致,模型编码不同,可覆盖父模型的模型管理器、数据排序规则、函数
多表继承 父子多表,父子间有隐式一对一关系,即父子模型都增加了一对一关联关系字段,同时父模型的字段被引用到子模型,且引用字段为只读字段,意味着子模型不可以直接更改父模型的字段值,子模型不继承父模型的模型管理器、数据排序规则、函数,子模型拥有自己的默认模型管理器、数据排序规则、函数。多表继承具有阻断效应,子模型无法自动多表继承父模型的存储父模型,需要显式声明多表继承父模型的存储父模型。
代理继承 代理模型继承并可覆盖父模型的模型管理器、数据排序规则、函数,同时可以使用排除继承字段和函数来达到不同场景不同视觉交互的效果。
继承的实践
抽象基类
注意: 只保存不希望为每个子模型重复键入的信息的模型,抽象基类模型不生成数据表存储数据,只供其他模型继承模型可继承域使用,抽象基类可以继承抽象基类。
场景一:抽象模型继承抽象模型
@Base
@Model.model(AbstractDemoModel.MODEL_MODEL)
@Model.Advanced(type = ModelTypeEnum.ABSTRACT)
@Model(displayName = "AbstractDemoModel")
public abstract class AbstractDemoModel extends IdModel{
public static final String MODEL_MODEL="demo.AbstractDemoModel";
@Base
@Field(displayName = "数据")
private String data;
}
场景二:存储模型继承抽象模型
@Model.model(PetShop.MODEL_MODEL)
@Model(displayName = "宠物店铺",summary="宠物店铺")
public class PetShop extends AbstractDemoModel {
public static final String MODEL_MODEL="demo.PetShop";
@Field(displayName = "店铺名称",required = true)
private String shopName;
@Field(displayName = "开店时间",required = true)
private Time openTime;
@Field(displayName = "闭店时间",required = true)
private Time closeTime;
}
查看数据库可以发现存储模型中新增了抽象模型中定义的字段 注意: 子模型与父模型的数据表相同,子模型继承父模型的字段与函数。存储模型之间的继承默认为扩展继承。
场景一: 新建我的商店、公共商店模型,用扩展继承,公共商店作为父类设置公共字段如:小店收入。
@Model.model(Shop.MODEL_MODEL)
@Model(displayName = "公共商店")
public class Shop extends IdModel {
public static final String MODEL_MODEL = "demo.Shop";
@Field(displayName = "收入")
private BigDecimal income;
}
@Model.model(MyShop.MODEL_MODEL)
@Model(displayName = "我的商店")
public class MyShop extends Shop {
public static final String MODEL_MODEL = "demo.MyShop";
@Field(displayName = "店铺名字")
private String name;
}
数据库展示 总结: 扩展继承 父子同表,模型在所有场景都有一致化的表现,意味着MyShop模型被扩展成了Shop模型,只有demo_core_shop一张表,新增了收入字段。父子模型的表名一致,模型编码不同,可覆盖父模型的模型管理器、数据排序规则、函数
多表继承
注意: 父模型不变,子模型获得父模型的可继承域,生成新的模型;父子模型不同表,子模型会建立与父模型的一对一关联关系字段(而不是交叉表),使用主键关联,同时子模型会通过一对一关联关系引用父模型的所有字段。多表继承父模型需要使用@Model.MultiTable
来标识,子模型需要使用@Model.MultiTableInherited
来标识。 **场景设计如下:**pet模型作为父模型,Cat、Dog分别继承Pet模型,并覆盖kind字段
@Model.model(Pet.MODEL_MODEL)
//typeField: 多表继承父模型中的类型字段编码
//用@Model.MultiTable(typeField = "kind"),申明为可多表继承父类,typeField指定为kind字段
@Model.MultiTable(typeField = "kind")
@Model(displayName = "宠物")
public class Pet extends IdModel {
public static final String MODEL_MODEL = "demo.Pet";
@Field(displayName = "品种名")
private String name;
@Field(displayName = "宠物分类")
private String kind;
}
//用@Model.MultiTableInherited(type = Dog.KIND_DOG),申明以多表继承模式继承PetType,覆盖kind字段
@Model.MultiTableInherited(type = Dog.KIND_DOG)
@Model.model(Dog.MODEL_MODEL)
@Model(displayName = "小狗")
public class Dog extends Pet {
public static final String MODEL_MODEL = "demo.Dog";
public static final String KIND_DOG = "DOG";
@Field(displayName = "小狗名字")
private String dogName;
@Field(displayName = "宠物分类",defaultValue = Dog.KIND_DOG)
private String kind;
}
//用@Model.MultiTableInherited(type = Cat.KIND_CAT),申明以多表继承模式继承Pet,覆盖kind字段
@Model.MultiTableInherited(type = Cat.KIND_CAT)
@Model.model(Cat.MODEL_MODEL)
@Model(displayName = "小猫")
public class Cat extends Pet {
public static final String MODEL_MODEL = "demo.Cat";
public static final String KIND_CAT = "CAT";
@Field(displayName = "小猫名字")
private String catName;
@Field(displayName = "宠物分类",defaultValue = Dog.KIND_DOG)
private String kind;
}
查看数据库 总结:父子模型不同表,子模型(cat,dog)会建立与父模型(pet)的一对一关联关系字段(而不是交叉表),使用主键关联,同时子模型会通过一对一关联关系引用父模型的所有字段。
代理继承
注意: 为原始模型创建代理,可以增删改查代理模型的实体数据,就像使用原始(非代理)模型一样。不同之处在于代理继承并不关注更改字段,可以更改代理中的元信息、函数和动作,而无需更改原始内容。一个代理模型必须仅能继承一个非抽象模型类。一个代理模型可以继承任意数量的没有定义任何模型字段的抽象模型类。一个代理模型也可以继承任意数量继承相同父类的代理模型。
场景一:一个代理模型继承一个存储模型
@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(displayName = "代理字段",required = true)
private String creater;
}
场景二:一个代理模型继承任意数量继承相同父类的代理模型。
- 新建【宠物店铺代理模型A】申明为代理模型
@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";
@Field.one2many
@Field(displayName = "商品列表")
@Field.Relation(relationFields = {"id"},referenceFields = {"shopId"})
private List<PetItem> items;
}
- 新建【宠物店铺代理模型B】申明为代理模型,用
@Model.Advanced(inherited ={PetShopProxy.MODEL_MODEL,PetShopProxyA.MODEL_MODEL})
声明继承多个同源代理模型,这样就实现了一个代理模型继承任意数量继承相同父类的代理模型。
@Model.model(PetShopProxyB.MODEL_MODEL)
@Model.Advanced(type = ModelTypeEnum.PROXY,inherited ={PetShopProxy.MODEL_MODEL,PetShopProxyA.MODEL_MODEL} )
@Model(displayName = "宠物店铺代理模型B",summary="宠物店铺代理模型B")
public class PetShopProxyB extends PetShop {
public static final String MODEL_MODEL="demo.PetShopProxyB";
@Field.one2many
@Field(displayName = "萌猫商品列表")
@Field.Relation(relationFields = {"id"},referenceFields = {"shopId"})
private List<PetCatItem> catItems;
}
临时继承
将父模型作为传输模型使用,并可以添加传输字段。