本文会介绍不同类型模型以及其简单的应用场景,方便大家理解不同类型模型的用途
模型分为元模型和业务模型。元数据是指描述应用程序运行所必需的数据、规则和逻辑的数据集;元模型是指用于描述内核元数据的一套模式集合;业务模型是指用于描述业务应用元数据的一套模式集合。
元模型分为模块域、模型域和函数域三个域。域的划分规则是根据元模型定义数据关联关系的离散性来判断,离散程度越小越聚集到一个域。在4.1.4【模块元数据详解】一文中介绍的ModuleDefinition就是元模型。而我们在开发中涉及的就是业务模型
一、模型类型
-
抽象模型:往往是提供公共能力和字段的模型,它本身不会直接用于构建协议和基础设施(如表结构等)。
-
传输模型:用于表现层和应用层之间的数据交互,本身不会存储,没有默认的数据管理器,只有数据构造器。
-
存储模型:存储模型用于定义数据表结构和数据的增删改查(数据管理器)功能,是直接与连接器进行交互的数据容器。
-
代理模型:用于代理存储模型的数据管理器能力的同时,扩展出非存储数据信息的交互功能的模型。
二、模型定义种类
模型定义就是模型描述,不同定义类型,代表计算描述模型的元数据的规则不同
-
静态模型定义:模型元数据不持久化、不进行模型定义的计算(默认值、主键、继承、关联关系)
-
静态计算模型定义:模型元数据不持久化但初始化时进行模型定义计算获得最终的模型定义
-
动态模型定义:模型元数据持久化且初始化时进行模型定义计算获得最终的模型定义
静态模型定义需要使用@Model.Static进行注解;静态计算模型定义使用@Model.Static(compute=true)进行注解;动态模型定义不注解@Model.Static注解。
三、安装与更新
使用@Model.model来配置模型的不可变更编码。模型一旦安装,无法在对该模型编码值进行修改,之后的模型配置更新会依据该编码进行查找并更新;如果仍然修改该注解的配置值,则系统会将该模型识别为新模型,存储模型会创建新的数据库表,而原表将会rename为废弃表。
如果模型配置了@Base注解,表明在【oinone的设计器】中该模型配置不可变更;如果字段配置了@Base注解,表明在【oinone的设计器】中该字段配置不可变更。
四、基础配置
模型基类
所有的模型都需要继承以下模型中的一种,来表明模型的类型,同时继承以下模型的默认数据管理器(详见3.3.3模型的数据管理器一节)。
-
继承BaseModel,构建存储模型,默认无id属性。
-
继承BaseRelation,构建多对多关系模型,默认无id属性。
-
继承TransientModel,构建临时模型(传输模型),临时模型没有数据管理器,也没有id属性。
-
继承EnhanceModel,构建数据源为ElasticSearch的增强模型。
快捷继承
-
继承IdModel,构建主键为id的模型。继承IdModel的模型会数据管理器会增加queryById方法(根据id查询单条记录)
-
继承CodeModel,构建带有唯一编码code的主键为id的模型。可以使用@Model.Code注解配置编码生成规则。也可以直接覆盖CodeModel的generateCode方法或者自定义新增的前置扩展点自定义编码生成逻辑。继承CodeModel的模型会数据管理器会增加queryByCode方法(根据唯一编码查询单条记录)
-
继承VersionModel,构建带有乐观锁,唯一编码code且主键为id的模型。
-
继承IdRelation,构建主键为id的多对多关系模型。
模型继承关系图
-
AbstractModel抽象基类是包含createDate创建时间、writeDate更新时间、createUid创建用户ID、writeUid更新用户ID、aggs聚合结果和activePks批量主键列表等基础字段的抽象模型。
-
TransientModel传输模型抽象基类是所有传输模型的基类,传输模型不存储,没有数据管理器。
-
TransientRelation传输关系模型是所有传输关系模型的基类,传输关系模型不存储,用于承载多对多关系,没有数据管理器。
-
BaseModel存储模型基类提供数据管理器功能,数据模型主键可以不是ID。
-
IdModel带id模型抽象基类,在BaseModel数据管理器基础之上提供根据ID查询、更新、删除数据的功能。
-
BaseRelation关系模型抽象基类用于承载多对多关系,是多对多关系的中间模型,数据模型主键可以不是ID。
-
IdRelation带id关系模型抽象基类,在BaseRelation数据管理器基础之上提供根据ID查询、更新、删除数据的功能。
-
CodeModel带code模型抽象基类,提供按配置生成业务唯一编码功能,根据code查询、更新、删除数据的功能。
-
EnhanceModel增强模型,提供全文检索能力。此模型会在4.1.25【框架之搜索引擎】一文中展开介绍。
五、抽象模型(举例)
抽象模型本身不会直接用于构建协议和基础设施(如表结构等),而是通过继承的机制供子模型复用其字段和函数。子模型可以是所有类型的模型。
比如demo模块要管理的一些公共模型字段,我们可以建一个AbstractDemoIdModel和AbstractDemoCodeModel,demo模块中的实体模型就可以继承它们。我们来为demo模块的模型统一增加一个数据状态这么一个字段,用做数据的生效与失效管理。
Step1 引入DataStatusEnum类
pamirs-demo-api的pom.xml包增加依赖,便于引入DataStatusEnum类,当然也可以自己建,这里只是oinone提供了统一的数据记录状态的枚举,以及相应的通用方法,这边就直接引入
<dependency>
<groupId>pro.shushi.pamirs.core</groupId>
<artifactId>pamirs-core-common</artifactId>
</dependency>
Step2 修改DemoModule
DataStatusEnum枚举类本身也会做为数据字典,以元数据的方式被管理起来,当一个模块依赖另一个模块的元数据相关对象,则需要改模块的模块依赖定义。为DemoModule增加CommonModule的依赖注解
package pro.shushi.pamirs.demo.api;
import org.springframework.stereotype.Component;
import pro.shushi.pamirs.boot.base.ux.annotation.action.UxRoute;
import pro.shushi.pamirs.boot.base.ux.annotation.navigator.UxHomepage;
import pro.shushi.pamirs.core.common.CommonModule;
import pro.shushi.pamirs.demo.api.model.PetShop;
import pro.shushi.pamirs.meta.annotation.Module;
import pro.shushi.pamirs.meta.base.PamirsModule;
import pro.shushi.pamirs.meta.common.constants.ModuleConstants;
@Component
@Module(
name = DemoModule.MODULE_NAME,
displayName = "oinoneDemo工程",
version = "1.0.0",
dependencies = {ModuleConstants.MODULE_BASE, CommonModule.MODULE_MODULE}
)
@Module.module(DemoModule.MODULE_MODULE)
@Module.Advanced(selfBuilt = true, application = true)
@UxHomepage(@UxRoute(PetShop.MODEL_MODEL))
public class DemoModule implements PamirsModule {
public static final String MODULE_MODULE = "demo_core";
public static final String MODULE_NAME = "DemoCore";
@Override
public String[] packagePrefix() {
return new String[]{ "pro.shushi.pamirs.demo"};
}
}
Step3 新建AbstractDemoCodeModel和AbstractDemoIdModel
并新增AbstractDemoIdModel和AbstractDemoCodeModel分别继承IdModel和CodeModel,实现IDataStatus接口不是必须的,刚好DataStatus有配套的通用逻辑,暂时也先加进去,具体使用会在本文的【代理模型】这段介绍
package pro.shushi.pamirs.demo.api.model;
import pro.shushi.pamirs.core.common.behavior.IDataStatus;
import pro.shushi.pamirs.core.common.enmu.DataStatusEnum;
import pro.shushi.pamirs.meta.annotation.Field;
import pro.shushi.pamirs.meta.annotation.Model;
import pro.shushi.pamirs.meta.annotation.sys.Base;
import pro.shushi.pamirs.meta.base.common.CodeModel;
import pro.shushi.pamirs.meta.enmu.ModelTypeEnum;
@Base
@Model.model(AbstractDemoCodeModel.MODEL_MODEL)
@Model.Advanced(type = ModelTypeEnum.ABSTRACT)
@Model(displayName = "AbstractDemoCodeModel")
public abstract class AbstractDemoCodeModel extends CodeModel implements IDataStatus {
public static final String MODEL_MODEL="demo.AbstractDemoCodeModel";
@Base
@Field.Enum
@Field(displayName = "数据状态",defaultValue = "DISABLED",required = true,summary = "作为基类给每一个继承模型增加一个数据状态字段")
private DataStatusEnum dataStatus;
}
package pro.shushi.pamirs.demo.api.model;
import pro.shushi.pamirs.core.common.behavior.IDataStatus;
import pro.shushi.pamirs.core.common.enmu.DataStatusEnum;
import pro.shushi.pamirs.meta.annotation.Field;
import pro.shushi.pamirs.meta.annotation.Model;
import pro.shushi.pamirs.meta.annotation.sys.Base;
import pro.shushi.pamirs.meta.base.IdModel;
import pro.shushi.pamirs.meta.enmu.ModelTypeEnum;
@Base
@Model.model(AbstractDemoIdModel.MODEL_MODEL)
@Model.Advanced(type = ModelTypeEnum.ABSTRACT)
@Model(displayName = "AbstractDemoIdModel")
public abstract class AbstractDemoIdModel extends IdModel implements IDataStatus {
public static final String MODEL_MODEL="demo.AbstractDemoIdModel";
@Base
@Field.Enum
@Field(displayName = "数据状态",defaultValue = "DISABLED",required = true,summary = "作为基类给每一个继承模型增加一个数据状态字段")
private DataStatusEnum dataStatus;
}
Step4 修改PetShop的父类
修改PetShop父类从IdMode变更为AbstractDemoIdModel
package pro.shushi.pamirs.demo.api.model;
import pro.shushi.pamirs.meta.annotation.Field;
import pro.shushi.pamirs.meta.annotation.Model;
import java.sql.Time;
@Model.model(PetShop.MODEL_MODEL)
@Model(displayName = "宠物店铺",summary="宠物店铺")
public class PetShop extends AbstractDemoIdModel {
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;
}
重启看效果
宠物商店的列表页面和修改页面都增加数据状态字段,如下图3-3-2-7及3-3-2-8所示:
六、存储模型
存储模型用于定义数据表结构和数据的增删改查(数据管理器)功能,是直接与连接器进行交互的数据容器。
PetShop就是一个存储模型,这里就不过多举例子介绍了
七、代理模型(举例)
代理模型是用于代理存储模型的数据管理器能力,同时又可以扩展出非存储数据信息的交互功能的模型。
如果我们PetShop模型要展示创建人昵称?那么就可以建一个PetShopProxy类来完成,代理模型增加的字段都是非存储字段,只用于交互包括展示或提交
Step1 引入PamirsUser类
pamirs-demo-api的pom.xml包增加依赖,便于引入PamirsUser类。
<dependency>
<groupId>pro.shushi.pamirs.core</groupId>
<artifactId>pamirs-user-api</artifactId>
</dependency>
Step2 新建PetShopProxy模型
新建一个PetShopProxy类继承PetShop,并声明类型为PROXY。同时增加一个creater字段
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;
}
Step3 修改DemoModule
PamirsUser模型隶属于UserModule。为DemoModule增加UserModule的依赖注解,同时修改DemoModule的homepage注解,默认进入PetShopProxy的管理页面
package pro.shushi.pamirs.demo.api;
import org.springframework.stereotype.Component;
import pro.shushi.pamirs.boot.base.ux.annotation.action.UxRoute;
import pro.shushi.pamirs.boot.base.ux.annotation.navigator.UxHomepage;
import pro.shushi.pamirs.core.common.CommonModule;
import pro.shushi.pamirs.demo.api.proxy.PetShopProxy;
import pro.shushi.pamirs.meta.annotation.Module;
import pro.shushi.pamirs.meta.base.PamirsModule;
import pro.shushi.pamirs.meta.common.constants.ModuleConstants;
import pro.shushi.pamirs.user.api.UserModule;
@Component
@Module(
name = DemoModule.MODULE_NAME,
displayName = "oinoneDemo工程",
version = "1.0.0",
dependencies = {ModuleConstants.MODULE_BASE, CommonModule.MODULE_MODULE, UserModule.MODULE_MODULE})
@Module.module(DemoModule.MODULE_MODULE)
@Module.Advanced(selfBuilt = true, application = true)
@UxHomepage(@UxRoute(PetShopProxy.MODEL_MODEL))
public class DemoModule implements PamirsModule {
public static final String MODULE_MODULE = "demo_core";
public static final String MODULE_NAME = "DemoCore";
@Override
public String[] packagePrefix() {
return new String[]{ "pro.shushi.pamirs.demo"};
}
}
Step4 新增PetShopProxyAction类
为了展示效果覆盖PetShopProxy默认从PetShop继承的数据管理器方法,但对于Action和Function这些这里不展开介绍,具体会3.4【oinone以函数为内在】和3.5【oinone以交互为外在】两章中介绍。
-
新建一个PetShopProxyAction类,Model.model设置为PetShopProxy,也就是把所有aciton和function都挂在PetShopProxy这个模型载体上
-
PetShopProxyAction继承DataStatusBehavior类配套dataStatus使用,定义一个【启用】serverAction,而启用逻辑则复用父类DataStatusBehavior的dataStatusEnable方法。
-
覆盖【queryPage】Function,该方法名为前后约定协议中列表查询的默认方法
package pro.shushi.pamirs.demo.core.action;
import org.springframework.stereotype.Component;
import pro.shushi.pamirs.core.common.behavior.impl.DataStatusBehavior;
import pro.shushi.pamirs.demo.api.proxy.PetShopProxy;
import pro.shushi.pamirs.meta.annotation.Action;
import pro.shushi.pamirs.meta.annotation.Function;
import pro.shushi.pamirs.meta.annotation.Model;
import pro.shushi.pamirs.meta.api.dto.condition.Pagination;
import pro.shushi.pamirs.meta.api.dto.wrapper.IWrapper;
import pro.shushi.pamirs.meta.constant.FunctionConstants;
import pro.shushi.pamirs.meta.enmu.FunctionOpenEnum;
import pro.shushi.pamirs.meta.enmu.FunctionTypeEnum;
@Model.model(PetShopProxy.MODEL_MODEL)
@Component
public class PetShopProxyAction extends DataStatusBehavior<PetShopProxy> {
@Override
protected PetShopProxy fetchData(PetShopProxy data) {
return data.queryById();
}
@Action(displayName = "启用")
public PetShopProxy dataStatusEnable(PetShopProxy data){
data = super.dataStatusEnable(data);
data.updateById();
return data;
}
@Function.Advanced(type= FunctionTypeEnum.QUERY)
@Function.fun(FunctionConstants.queryPage)
@Function(openLevel = {FunctionOpenEnum.API})
public Pagination<PetShopProxy> queryPage(Pagination<PetShopProxy> page, IWrapper<PetShopProxy> queryWrapper){
Pagination<PetShopProxy> result = new PetShopProxy().queryPage(page,queryWrapper);
new PetShopProxy().listFieldQuery(result.getContent(),PetShopProxy::getCreater);
return result;
}
}
Step5 重启看效果
宠物商店的列表页面和修改页面都增加数据状态字段,如下图3-3-24所示:
多次点击启用会报对应的错误。当然如果场景不需要,则没有必要复用DataStatusBehavior逻辑,也就不需要实现IDataStatus接口了,启动按钮只在禁用状态展示在后续的教程中会学到
八、传输模型(举例)
由于传输模型没有默认的数据管理器,只有数据构造器,所以在不自定义动作的情况下,传输模型可以打开详情页、新增表单和修改表单和列表页,但是所有的动作全部为窗口动作。传输模型本身不会存储,如果是关联关系字段关联传输模型,可以将传输模型序列化存储在模型的关联关系字段上。因为没有数据管理器,所以传输模型的列表页没有分页能力。
场景举例:如果我们想批量修改PetShop的数据状态,那么我们需要在列表页选中数据记录点击【批量修改】跳转到一个批量修改页面,选择要修改的数据状态,并点确认提交。那么传输模型就可以承载批量修改页面、数据以及操作的载体模型。
Step1 新建PetShopBatchUpdate模型
新建一个PetShopBatchUpdate类继承TransientModel,同时增加petShopList和dataStatus字段用于接收页面传值
package pro.shushi.pamirs.demo.api.tmodel;
import pro.shushi.pamirs.core.common.enmu.DataStatusEnum;
import pro.shushi.pamirs.demo.api.proxy.PetShopProxy;
import pro.shushi.pamirs.meta.annotation.Field;
import pro.shushi.pamirs.meta.annotation.Model;
import pro.shushi.pamirs.meta.base.TransientModel;
import java.util.List;
@Model.model(PetShopBatchUpdate.MODEL_MODEL)
@Model(displayName = "批量修改宠物商店数据状态",summary = "批量修改宠物商店数据状态")
public class PetShopBatchUpdate extends TransientModel {
public static final String MODEL_MODEL="demo.PetShopBatchUpdate";
@Field(displayName = "数据状态",required = true)
private DataStatusEnum dataStatus;
@Field(displayName = "宠物商店列表",required = true)
@Field.many2many
private List<PetShopProxy> petShopList;
}
图3-3-2-15 新建PetShopBatchUpdate模型
Step2 新增PetShopBatchUpdateAction类
传输模型没有默认的数据管理器,只有数据构造器(construct)。
-
新建一个PetShopBatchUpdateAction类,Model.model设置为PetShopBatchUpdate,也就是把所有Aciton和Function都挂在PetShopBatchUpdateAction这个模型载体上
-
覆盖数据构造器(construct),接收从宠物商店列表多选带过来的数据参数,非PetShopBatchUpdate本模型参数不能放第一个,用List petShopList来接收,进行数据组装逻辑处理,对应数据也是由PetShopBatchUpdate来承载返回给PetShopBatchUpdate的Form编辑页。
-
定义一个【确定】serverAction,绑定PetShopBatchUpdate的Form编辑页。点击确定则批量修改Form页面中的宠物商店列表为指定的数据状态
package pro.shushi.pamirs.demo.core.action;
import org.springframework.stereotype.Component;
import pro.shushi.pamirs.demo.api.proxy.PetShopProxy;
import pro.shushi.pamirs.demo.api.tmodel.PetShopBatchUpdate;
import pro.shushi.pamirs.meta.annotation.Action;
import pro.shushi.pamirs.meta.annotation.Function;
import pro.shushi.pamirs.meta.annotation.Model;
import pro.shushi.pamirs.meta.enmu.ActionContextTypeEnum;
import pro.shushi.pamirs.meta.enmu.FunctionOpenEnum;
import pro.shushi.pamirs.meta.enmu.FunctionTypeEnum;
import pro.shushi.pamirs.meta.enmu.ViewTypeEnum;
import java.util.List;
@Model.model(PetShopBatchUpdate.MODEL_MODEL)
@Component
public class PetShopBatchUpdateAction {
@Function(openLevel = FunctionOpenEnum.API)
@Function.Advanced(type= FunctionTypeEnum.QUERY)
public PetShopBatchUpdate construct(PetShopBatchUpdate petShopBatchUpdate, List<PetShopProxy> petShopList){
PetShopBatchUpdate result = new PetShopBatchUpdate();
//前端传递只有id,补全下数据
petShopList = FetchUtil.fetchList(petShopList);
//补全创建者字段
new PetShopProxy().listFieldQuery(petShopList,PetShopProxy::getCreater);
result.setPetShopList(petShopList);
return result;
}
@Action(displayName = "确定",bindingType = ViewTypeEnum.FORM,contextType = ActionContextTypeEnum.SINGLE)
public PetShopBatchUpdate conform(PetShopBatchUpdate data){
List<PetShopProxy> proxyList = data.getPetShopList();
for(PetShopProxy petShopProxy:proxyList){
petShopProxy.setDataStatus(data.getDataStatus());
}
new PetShopProxy().updateBatch(proxyList);
return data;
}
}
Step3 注解式初始化ViewAction窗口动作
在PetShopProxyAction增加类注解,并注释掉DemoModuleMetaDataEditor的viewActionInit方法体代码。用UxRouteButton注解来申明一个ViewAction。更多Ux系列注解详见3.5.4【Ux注解详解】一文
-
@Model.model(PetShopProxy.MODEL_MODEL),代表UxRouteButton申明viewAction所在模型
-
@UxRoute.model代表viewAction的resModel,路由的目标模型
@Model.model(PetShopProxy.MODEL_MODEL)
@UxRouteButton(action = @UxAction(name = "demo_petShop_batch_update", label = "批量更新数据状态",contextType = ActionContextTypeEnum.SINGLE_AND_BATCH), value = @UxRoute(model = PetShopBatchUpdate.MODEL_MODEL, viewType = ViewTypeEnum.FORM,openType = ActionTargetEnum.DIALOG,viewName= ViewConstants.Name.formView))
@Component
public class PetShopProxyAction extends DataStatusBehavior<PetShopProxy> {}
Step4 重启看效果
Oinone社区 作者:史, 昂原创文章,如若转载,请注明出处:https://doc.oinone.top/oio4/9233.html
访问Oinone官网:https://www.oinone.top获取数式Oinone低代码应用平台体验