3.3.4 模型的继承

在我们的很多项目中,客户都是有个性化需求的,就像我们不能找到两件一模一样的东西,何况是企业的经营与管理思路,多少都会有差异。常规的方式只能去修改标准产品的逻辑来适配客户的需求。导致后续标品维护非常困难。而在介绍完这节以后是不是让你更加清晰认知到我们2.4.2【oinone独特性之每一个需求都可以是一个模块】一文中所表达的特性带来的好处呢?

一、继承方式

继承方式可以分为五种:

  • 抽象基类ABSTRACT,只保存不希望为每个子模型重复键入的信息的模型,抽象基类模型不生成数据表存储数据,只供其他模型继承模型可继承域使用,抽象基类可以继承抽象基类。

  • 扩展继承EXTENDS,子模型与父模型的数据表相同,子模型继承父模型的字段与函数。存储模型之间的继承默认为扩展继承。

  • 多表继承MULTI_TABLE,父模型不变,子模型获得父模型的可继承域生成新的模型;父子模型不同表,子模型会建立与父模型的一对一关联关系字段(而不是交叉表),使用主键关联,同时子模型会通过一对一关联关系引用父模型的所有字段。多表继承父模型需要使用@Model.MultiTable来标识,子模型需要使用@Model.MultiTableInherited来标识。

  • 代理继承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进行配置

扩展继承 父子同表,模型在所有场景都有一致化的表现,意味着原模型被扩展成了新模型,父子模型的表名一致,模型编码不同,可覆盖父模型的模型管理器、数据排序规则、函数

多表继承 父子多表,父子间有隐式一对一关系,即父子模型都增加了一对一关联关系字段,同时父模型的字段被引用到子模型,且引用字段为只读字段,意味着子模型不可以直接更改父模型的字段值,子模型不继承父模型的模型管理器、数据排序规则、函数,子模型拥有自己的默认模型管理器、数据排序规则、函数。多表继承具有阻断效应,子模型无法自动多表继承父模型的存储父模型,需要显式声明多表继承父模型的存储父模型。

代理继承 代理模型继承并可覆盖父模型的模型管理器、数据排序规则、函数,同时可以使用排除继承字段和函数来达到不同场景不同视觉交互的效果。

3.3.4 模型的继承

图3-3-4-1 继承的使用场景

四、抽象基类(举例)

参考前文中3.3.2【模型的类型】一文中关于抽象模型的介绍

五、多表继承(举例)

场景设计如下

3.3.4 模型的继承

图3-3-4-2 多表继承设计场景

Step1 新建宠物品种、宠狗品种和萌猫品种模型

  1. 新建宠物品种模型,用@Model.MultiTable(typeField = "kind"),申明为可多表继承父类,typeField指定为kind字段
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 = "品种名")
    private String name;

    @Field(displayName = "宠物分类")
    private String kind;
}

图3-3-4-3 多表继承示例代码

  1. 新建宠狗品种模型,用@Model.MultiTableInherited(type = PetDogType.KIND_DOG),申明以多表继承模式继承PetType,覆盖kind字段(用defaultValue设置默认值,用invisible = true设置为前端不展示),更多模块元数据以及模型字段元数据配置详见4.1.6【模型之元数据详解】一文
package pro.shushi.pamirs.demo.api.model;

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

@Model.MultiTableInherited(type = PetDogType.KIND_DOG)
@Model.model(PetDogType.MODEL_MODEL)
@Model(displayName="宠狗品种",labelFields = {"name"})
public class PetDogType extends PetType {

    public static final String MODEL_MODEL="demo.PetDogType";
    public static final String KIND_DOG="DOG";

    @Field(displayName = "宠物分类",defaultValue = PetDogType.KIND_DOG,invisible = true)
    private String kind;
}

图3-3-4-4 多表继承示例代码

  1. 新建萌猫品种模型,用@Model.MultiTableInherited(type = PetCatType.KIND_CAT),申明以多表继承模式继承PetType,覆盖kind字段(用defaultValue设置默认值,用invisible = true设置为前端不展示),并新增一个CatShapeEnum枚举类型的字段shape
package pro.shushi.pamirs.demo.api.enumeration;

import pro.shushi.pamirs.meta.annotation.Dict;
import pro.shushi.pamirs.meta.common.enmu.BaseEnum;

@Dict(dictionary = CatShapeEnum.DICTIONARY,displayName = "萌猫体型")
public class CatShapeEnum extends BaseEnum<CatShapeEnum,Integer> {

    public static final String DICTIONARY ="demo.CatShapeEnum";

    public final static CatShapeEnum BIG =create("BIG",1,"大","大");
    public final static CatShapeEnum SMALL =create("SMALL",2,"小","小");
}

图3-3-4-5 多表继承示例代码

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

import pro.shushi.pamirs.demo.api.enumeration.CatShapeEnum;
import pro.shushi.pamirs.meta.annotation.Field;
import pro.shushi.pamirs.meta.annotation.Model;

@Model.MultiTableInherited(type = PetCatType.KIND_CAT)
@Model.model(PetCatType.MODEL_MODEL)
@Model(displayName="萌猫品种",labelFields = {"name"})
public class PetCatType extends PetType {

    public static final String MODEL_MODEL="demo.PetCatType";
    public static final String KIND_CAT="CAT";

    @Field(displayName = "宠物分类",defaultValue = PetCatType.KIND_CAT,invisible = true)
    private String kind;

    @Field.Enum
    @Field(displayName = "宠物体型")
    private CatShapeEnum shape;
}

图3-3-4-6 多表继承示例代码

Step2 配置菜单后,重启查看效果

  1. 为了更好帮助大家更好地理解,这里先把对应的菜单配置上,从页面来看看效果。下面以注解方式进行菜单配置,更多详见3.5.1【构建第一个Menu】一文。
package pro.shushi.pamirs.demo.core.init;

import pro.shushi.pamirs.boot.base.constants.ViewActionConstants;
import pro.shushi.pamirs.boot.base.ux.annotation.action.UxRoute;
import pro.shushi.pamirs.boot.base.ux.annotation.navigator.UxMenu;
import pro.shushi.pamirs.boot.base.ux.annotation.navigator.UxMenus;
import pro.shushi.pamirs.demo.api.model.*;

@UxMenus
public class DemoMenus implements ViewActionConstants {
    @UxMenu("宠物品种")@UxRoute(PetType.MODEL_MODEL) class PetTypeMenu{}
    @UxMenu("萌猫品种")@UxRoute(PetCatType.MODEL_MODEL) class CatTypeMenu{}
    @UxMenu("宠狗品种")@UxRoute(PetDogType.MODEL_MODEL) class DogTypeMenu{}
}

图3-3-4-7 对应菜单配置

  1. 在宠狗品种中新增、修改、列表都隐藏了品种类型字段,并附上了默认值DOG

3.3.4 模型的继承

图3-3-4-8 宠狗品种新增页

3.3.4 模型的继承

图3-3-4-9 宠狗品种列表页

3.3.4 模型的继承

图3-3-4-10 宠狗品种数据表

  1. 在萌猫品种中新增、修改、列表都增加宠物体型枚举字段,并且隐藏了品种类型字段,并附上了默认值CAT

3.3.4 模型的继承

图3-3-4-11 萌猫品种新增页

3.3.4 模型的继承

图3-3-4-12 萌猫品种列表页

3.3.4 模型的继承

图3-3-4-13 萌猫品种数据表

Step3 为宠狗和萌猫品种模型实现默认读写扩展点,保证父子表数据同步

多表继承默认父子表是不同步的,不过oinone为每一个模型的默认数据管理器都提供的默认读写扩展点,我们可以主动利用这个特性来进行手动同步,更多扩展点知识详见3.4.3【函数相关特性】部分中的扩展点一文,会介绍除了利用系统默认提供的扩展点,还有如何自定义扩展。

  1. PetCatType和PetDogType都需要实现扩展点来完成与父模型的同步,所以这里把相同逻辑做了提取AbstractPetTypeExtPoint
package pro.shushi.pamirs.demo.core.extpoint;

import pro.shushi.pamirs.core.common.CopyHelper;
import pro.shushi.pamirs.demo.api.model.PetType;
import pro.shushi.pamirs.framework.connectors.data.sql.Pops;
import pro.shushi.pamirs.meta.api.Models;
import pro.shushi.pamirs.meta.base.extpoint.DefaultReadWriteExtPoint;

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

public abstract class AbstractPetTypeExtPoint  extends DefaultReadWriteExtPoint {

    public T createBefore(T data) {
        data.construct();
        return data;
    }

    public T updateBefore(T data) {
        return data;
    }

    public T createAfter(T data) {
        CopyHelper.simpleReplace(data, new PetType()).create();
        return data;
    }

    public T updateAfter(T data) {
        CopyHelper.simpleReplace(data, new PetType()).updateById();
        return data;
    }

    public List deleteBefore(List data) {
        List petTypeIdList = new ArrayList();
        for(T item:data){
            petTypeIdList.add(item.getId());
        }
        Models.data().deleteByWrapper(Pops.lambdaQuery().from(PetType.MODEL_MODEL).in(PetType::getId,petTypeIdList));
        return data;
    }
}

图3-3-4-14 相同逻辑提取AbstractPetTypeExtPoint

  1. PetCatTypeExtPoint继承AbstractPetTypeExtPoint类,并通过@Ext(PetCatType.class)申明扩展点所扩展函数所在类为PetCatType,并@ExtPoint.Implement申明对应扩展点的实现方法实例
package pro.shushi.pamirs.demo.core.extpoint;

import pro.shushi.pamirs.demo.api.model.PetCatType;
import pro.shushi.pamirs.meta.annotation.Ext;
import pro.shushi.pamirs.meta.annotation.ExtPoint;

import java.util.List;

@Ext(PetCatType.class)
public class PetCatTypeExtPoint extends AbstractPetTypeExtPoint{

    @Override
    @ExtPoint.Implement
    public PetCatType createBefore(PetCatType data) {
        return super.createBefore(data);
    }
    @Override
    @ExtPoint.Implement
    public PetCatType updateBefore(PetCatType data) {
        return super.updateBefore(data);
    }
    @Override
    @ExtPoint.Implement
    public PetCatType createAfter(PetCatType data) {
        return super.createAfter(data);
    }
    @Override
    @ExtPoint.Implement
    public PetCatType updateAfter(PetCatType data) {
        return super.updateAfter(data);
    }
    @Override
    @ExtPoint.Implement
    public List deleteBefore(List data) {
        return super.deleteBefore(data);
    }
}

图3-3-4-15 PetCatTypeExtPoint继承AbstractPetTypeExtPoint类

  1. PetDogTypeExtPoint继承AbstractPetTypeExtPoint类,并通过@Ext(PetDogType.class)申明扩展点所扩展函数所在类为PetDogType,并@ExtPoint.Implement申明对应扩展点的实现方法实例
package pro.shushi.pamirs.demo.core.extpoint;

import pro.shushi.pamirs.demo.api.model.PetDogType;
import pro.shushi.pamirs.meta.annotation.Ext;
import pro.shushi.pamirs.meta.annotation.ExtPoint;

import java.util.List;

@Ext(PetDogType.class)
public class PetDogTypeExtPoint extends AbstractPetTypeExtPoint{

    @Override
    @ExtPoint.Implement
    public PetDogType createBefore(PetDogType data) {
        return super.createBefore(data);
    }
    @Override
    @ExtPoint.Implement
    public PetDogType updateBefore(PetDogType data) {
        return super.updateBefore(data);
    }
    @Override
    @ExtPoint.Implement
    public PetDogType createAfter(PetDogType data) {
        return super.createAfter(data);
    }
    @Override
    @ExtPoint.Implement
    public PetDogType updateAfter(PetDogType data) {
        return super.updateAfter(data);
    }
    @Override
    @ExtPoint.Implement
    public List deleteBefore(List data) {
        return super.deleteBefore(data);
    }
}

图3-3-4-16 PetDogTypeExtPoint继承AbstractPetTypeExtPoint类

Step4 重启查看效果:在萌猫品种和宠狗品种下新增、修改、删除,则会在宠物品种下看到对应记录变化

3.3.4 模型的继承

图3-3-4-17 父模型宠物品种列表页

3.3.4 模型的继承

图3-3-4-18 父模型宠物品种数据表

六、扩展继承(举例)

场景设计(如下图3-3-55所示)

3.3.4 模型的继承

图3-3-4-19 扩展继承场景设计

Step1 新建宠物商品、宠狗商品和萌猫商品模型,并配置菜单

  1. 新建宠物商品模型为普通存储模型,用扩展继承父类设置公共字段如:店铺,品种等,注意type字段类型为PetType
package pro.shushi.pamirs.demo.api.model;

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

import java.math.BigDecimal;

@Model.model(PetItem.MODEL_MODEL)
@Model(displayName = "宠物商品",summary="宠物商品")
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)
    private PetShop shop;

    @Field(displayName = "品种")
    @Field.many2one
    private PetType type;
}

图3-3-4-20 新建宠物商品模型为普通存储模型

  1. 新建宠狗商品模型继承宠物商品模型,注意这里把type字段类型覆盖为PetType的子类PetDogType,并新增原产地字段provenance
package pro.shushi.pamirs.demo.api.model;

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

@Model.model(PetDogItem.MODEL_MODEL)
@Model(displayName = "宠狗商品",summary="宠狗商品")
public class PetDogItem extends PetItem{

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

    @Field(displayName = "品种")
    @Field.many2one
    private PetDogType type;

    @Field(displayName = "原产地")
    private String provenance;
}

图3-3-4-21 新建宠狗商品模型继承宠物商品模型

  1. 新建萌猫商品模型继承宠物商品模型,注意这里把type字段类型覆盖为PetType的子类PetCatType。
package pro.shushi.pamirs.demo.api.model;

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

@Model.model(PetCatItem.MODEL_MODEL)
@Model(displayName = "萌猫商品",summary="萌猫商品")
public class PetCatItem extends PetItem{
    public static final String MODEL_MODEL="demo.PetCatItem";

    @Field(displayName = "品种")
    @Field.many2one
    private PetCatType type;
}

图3-3-4-22 新建萌猫商品模型继承宠物商品模型

  1. 为了更好帮助大家更好地理解,这里先把对应的菜单配置上,从页面来看看效果。下面以注解方式进行菜单配置,更多详见3.5.1【构建第一个Menu】一文。
package pro.shushi.pamirs.demo.core.init;

import pro.shushi.pamirs.boot.base.constants.ViewActionConstants;
import pro.shushi.pamirs.boot.base.ux.annotation.action.UxRoute;
import pro.shushi.pamirs.boot.base.ux.annotation.navigator.UxMenu;
import pro.shushi.pamirs.boot.base.ux.annotation.navigator.UxMenus;
import pro.shushi.pamirs.demo.api.model.*;

@UxMenus
public class DemoMenus implements ViewActionConstants {
    @UxMenu("商品管理")@UxRoute(PetItem.MODEL_MODEL) class ItemMenu{}
    @UxMenu("宠狗商品")@UxRoute(PetDogItem.MODEL_MODEL) class DogItemMenu{}
    @UxMenu("萌猫商品")@UxRoute(PetCatItem.MODEL_MODEL) class CatItemMenu{}
    @UxMenu("宠物品种")@UxRoute(PetType.MODEL_MODEL) class PetTypeMenu{}
    @UxMenu("萌猫品种")@UxRoute(PetCatType.MODEL_MODEL) class CatTypeMenu{}
    @UxMenu("宠狗品种")@UxRoute(PetDogType.MODEL_MODEL) class DogTypeMenu{}
}

图3-3-4-23 以注解方式进行菜单配置

至此模型就建完了,还是很简单的吧

Step2 分别新建三条宠物商品、宠狗商品和萌猫商品记录,查看效果

  1. 父模型宠物商品的商品管理可以看到品类中可选柴犬、加菲猫,数据来源是品种的父模型PetType,店铺下拉框展示为"-",因为店铺模型没有配置label字段,可以在店铺模型的注解上增加“@Model(displayName = "宠物店铺",summary="宠物店铺",labelFields = {"shopName"})”,即可解决。这里先选择第一个往下执行

3.3.4 模型的继承

图3-3-4-24 宠物商品管理

  1. 父模型宠狗商品的商品管理可以看到品类中可选柴犬,数据来源是品种的子模型PetDogType,而且新增源产地字段;

3.3.4 模型的继承

图3-3-4-25 父模型宠狗商品管理

  1. 父模型萌猫商品的商品管理可以看到品类中可选加菲猫,数据来源是品种的子模型PetCatType;

3.3.4 模型的继承

图3-3-4-26 父模型萌猫商品管理

Step3 分别观察宠物商品、宠狗商品和萌猫商品的列表页,我们会发现数据记录都是三条,但是展示字段会随着模型不同而有差异

  1. 父页面在品种一列中可以显示所有品种名称

3.3.4 模型的继承

图3-3-4-27 父页面在品种一列中可以显示所有品种名称

  1. 宠狗页面在品种一列中只能显示PetDogType的品种名称、但多了原产地字段

3.3.4 模型的继承

图3-3-4-28 宠狗页面品种列展示

  1. 萌猫页面在品种一列中只能显示PetCatType的品种名称

3.3.4 模型的继承

图3-3-4-29 萌猫页面品种列中只展示PetCatType的品种名称

  1. 对应的数据库表:数据都在一张记录表中,新增了原产地字段

3.3.4 模型的继承

图3-3-4-30 对应数据库表

Step4 顺带优化下宠物店铺的展示Labels

  1. 给PetShop增加@Model(labelFields ={"shopName"} )注解,labelFields是为模型的“数据标题, 用于前端展示”,其默认值为name,但PetShop没有name字段,所以我们列表上展示不出来。更多元数据见4.1.6【模型之元数据详解】一文;
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="宠物店铺",labelFields ={"shopName"} )
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-4-31 给PetShop增加@Model(labelFields ={shopName} )注解

  1. 重启查看效果,店铺字段可以展示出来

3.3.4 模型的继承

图3-3-4-32 店铺字段可展示

Step5 思考在扩展继承模式下数据隔离问题

多表继承需要自己搞定父、子模型的数据同步问题,同表继承则需要自己搞定数据隔离问题。我们可以重写queryPage这个Function,如我们在3.3.2【模型类型】一文中介绍“代理模型类型”的部分时,涉及的PetShopProxyAction就覆盖了queryPage。这里举例PetCatItem的queryPage覆盖,PetDogItem留给大家自行练习

  1. 给PetItem显示增加一个typeId字段,方便在PetCatItemAction中用于过滤条件使用。为什么说显示增加呢?因为type字段是一个many2one的字段在没有配置@Field.Relation的情况下,oinone会为PetItem模型自动推断出一个typeId字段去关联PetType模型的Id。@Field.Relation的relationFields为本模型的关联字段,referenceFields为目标模型关联字段。如符合推断规范可以不配置@Field.Relation,就如这个场景配不配效果是一样的。还是那句话“更多元数据详见4.1.6【模型之元数据详解】一文”;
package pro.shushi.pamirs.demo.api.model;

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

import java.math.BigDecimal;

@Model.model(PetItem.MODEL_MODEL)
@Model(displayName = "宠物商品",summary="宠物商品")
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)
    private PetShop shop;

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

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

图3-3-4-33 给PetItem显示增加一个typeId字段

  1. 覆盖PetCatItem名为“queryFilters”的函数,增加typeId的过滤条件为PetCatType表对应的Id列表,则针对PetCatItem的所有查询都会自动加上“queryFilters”的函数返回的过滤条件
package pro.shushi.pamirs.demo.core.action;

import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import pro.shushi.pamirs.core.common.behavior.impl.DataStatusBehavior;
import pro.shushi.pamirs.demo.api.model.PetCatItem;
import pro.shushi.pamirs.demo.api.model.PetCatType;
import pro.shushi.pamirs.framework.connectors.data.sql.Pops;
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;

import java.util.List;
import java.util.stream.Collectors;

@Model.model(PetCatItem.MODEL_MODEL)
@Component
public class PetCatItemAction extends DataStatusBehavior {

    @Override
    protected PetCatItem fetchData(PetCatItem data) {
        return data.queryById();
    }
    @Action(displayName = "启用")
    public PetCatItem dataStatusEnable(PetCatItem data){
        data = super.dataStatusEnable(data);
        data.updateById();
        return data;
    }

    @Function.Advanced(displayName = "查询模型数据的默认过滤条件", type = FunctionTypeEnum.QUERY, managed = true)
    @Function(openLevel = {LOCAL})
    public String queryFilters() {
        StringBuilder sqlWhereCondition = new StringBuilder();
        List typeList = new PetCatType().queryList();
        if(!CollectionUtils.isEmpty(typeList)){
//          sqlWhereCondition.append("type_id");
            sqlWhereCondition.append(PStringUtils.fieldName2Column(LambdaUtil.fetchFieldName(PetCatItem::getTypeId)));
            sqlWhereCondition.append(StringUtils.SPACE).append(SqlConstants.IN).append(CharacterConstants.LEFT_BRACKET);
            for(PetCatType petCatType: typeList){
                sqlWhereCondition.append(petCatType.getId()).append(CharacterConstants.SEPARATOR_COMMA);
            }
            sqlWhereCondition.deleteCharAt(sqlWhereCondition.lastIndexOf(CharacterConstants.SEPARATOR_COMMA));
            sqlWhereCondition.append(StringUtils.SPACE).append(CharacterConstants.RIGHT_BRACKET);
        }
        return sqlWhereCondition.toString();
    }
}

图3-3-4-34 覆盖函数的代码示例

  1. 重启查看效果,萌猫商品只能看到PetCatType对应的数据记录了,赶紧自己动手试试PetDogItem的改造吧

3.3.4 模型的继承

图3-3-4-35 重启查看效果

七、代理继承(举例)

代理模型在3.3.2【模型的类型】一文中有介绍过,代理模型看名字就知道其本身是通过继承方式代理另一个存储模型,这里不过多介绍。我们来尝试一下它继承的特殊性“一个代理模型也可以继承任意数量继承相同父类的代理模型”

场景设计(如下图3-3-72所示)

3.3.4 模型的继承

图3-3-4-36 代理继承场景设计

Step1 新建宠物店铺代理模型A和宠物店铺代理模型B,同时修改PetItem

  1. 新建宠物店铺代理模型A申明为代理模型,新增一个one2many字段items,用@Field.Relation申明关联字段
package pro.shushi.pamirs.demo.api.proxy;

import pro.shushi.pamirs.demo.api.model.PetItem;
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;

import java.util.List;

@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 items;
}

图3-3-4-37 新建宠物店铺代理模型A申明为代理模型

  1. 新建宠物店铺代理模型B申明为代理模型,用 @Model.Advanced(inherited ={PetShopProxy.MODEL_MODEL,PetShopProxyA.MODEL_MODEL})声明继承多个同源代理模型,并且新增一个one2many字段catItems用@Field.Relation申明关联字段
package pro.shushi.pamirs.demo.api.proxy;

import pro.shushi.pamirs.demo.api.model.PetCatItem;
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 java.util.List;

@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 catItems;
}

图3-3-4-38 新建宠物店铺代理模型B申明为代理模型

  1. 修改PetItem增加一个labelFields={"itemName"}注解,给PetItem显示增加一个shopId字段方便,在Service中用于过滤条件使用。
package pro.shushi.pamirs.demo.api.model;

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

import java.math.BigDecimal;

@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)
    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;
}

图3-3-4-39 PetItem代码修改示例

Step2 覆盖PetShopProxyB的queryPage和queryOne函数

PetShopProxyB的queryPage和queryOne都查catItems\creater\items三个字段

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

import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import pro.shushi.pamirs.core.common.behavior.impl.DataStatusBehavior;
import pro.shushi.pamirs.demo.api.model.PetShop;
import pro.shushi.pamirs.demo.api.proxy.PetShopProxy;
import pro.shushi.pamirs.demo.api.proxy.PetShopProxyB;
import pro.shushi.pamirs.framework.faas.utils.ArgUtils;
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(PetShopProxyB.MODEL_MODEL)
@Component
public class PetShopProxyBAction  {

    @Function.Advanced(type= FunctionTypeEnum.QUERY)
    @Function.fun(FunctionConstants.queryPage)
    @Function(openLevel = {FunctionOpenEnum.API})
    public Pagination queryPage(Pagination page, IWrapper queryWrapper){
        Pagination result = new PetShopProxy().queryPage(page,queryWrapper);
        if(!CollectionUtils.isEmpty(result.getContent())) {
            //自身继承【宠物店铺】后扩展的字段catItems
            new PetShopProxyB().listFieldQuery(result.getContent(),PetShopProxyB::getCatItems);
            //继承【宠物店铺代理模型】而来的字段creater,java语言限制【宠物店铺代理模型B】没有getCreate()方法
            new PetShopProxyB().listFieldQuery(result.getContent(),"creater");
            //继承【宠物店铺代理模型A】而来的字段items,java语言限制【宠物店铺代理模型B】没有getItems()方法
            new PetShopProxyB().listFieldQuery(result.getContent(),"items");
        }
        return result;
    }
    @Function.Advanced(type= FunctionTypeEnum.QUERY)
    @Function.fun(FunctionConstants.queryByEntity)
    @Function(openLevel = {FunctionOpenEnum.API})
    public PetShopProxyB queryOne(PetShopProxyB query){
        query = query.queryById();
        //自身继承【宠物店铺】后扩展的字段catItems
        query.fieldQuery(PetShopProxyB::getCatItems);
        //继承【宠物店铺代理模型】而来的字段creater,java语言限制【宠物店铺代理模型B】没有getCreate()方法
        query.fieldQuery("creater");
        //继承【宠物店铺代理模型A】而来的字段items,java语言限制【宠物店铺代理模型B】没有getItems()方法
        query.fieldQuery("items");
        return query;
    }
}

图3-3-4-40 函数覆盖代码示例

Step3 配置菜单,并重启看效果

  1. 配置菜单:请参考本节前面介绍自行增加三个菜单商店管理、商店管理A、商店管理B分别对应宠物店铺代理模型、宠物店铺代理模型A、宠物店铺代理模型B的管理入口

  2. 宠物店铺代理模型覆盖了queryPage方法但没有覆盖queryOne方法,所以列表页有显示新增字段创建者,但详情页没有。具体详见3.3.2【模型的类型】一文中的“代理模型”介绍部分;

3.3.4 模型的继承

图3-3-4-41 宠物店铺代理模型的列表页

3.3.4 模型的继承

图3-3-4-42 宠物店铺代理模型的详情页

  1. 宠物店铺代理模型A只增加了一个商品列表字段,没有覆盖对应query相关方法,所以列表页有这个字段但没有值;

3.3.4 模型的继承

图3-3-4-43 宠物店铺代理模型A的列表页

  1. 宠物店铺代理模型B因为同源多表继承了宠物店铺代理模型和宠物店铺代理模型A所以它拥有商品列表、萌猫商品列表、创建者三个字段,并且同时覆盖queryPage和queryOne函数,所以列表和详情都有值。

3.3.4 模型的继承

图3-3-4-44 宠物店铺代理模型B的列表页

3.3.4 模型的继承

图3-3-4-45 宠物店铺代理模型B的详情页

八、临时继承(举例)

参考前文3.3.2【模型的类型】一文中对传输模型的介绍,差别在于一个继承抽象基类,一个继承其他模型类型,不管什么类型都只是复用父类的字段,自己还是传输模型

九、总结

在我们的很多项目中,客户都是有个性化需求的,就像我们不能找到两件一模一样的东西,何况是企业的经营与管理思路,多少都会有差异。常规的方式只能去修改标准产品的逻辑来适配客户的需求。导致后续标品维护非常困难。而在介绍完这节以后是不是让你更加清晰认知到我们2.4.2【oinone独特性之每一个需求都可以是一个模块】一文中所表达的特性带来的好处呢?

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

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

(1)
史, 昂史, 昂
上一篇 2024年5月23日 am9:42
下一篇 2024年5月23日 am9:49

相关推荐

  • 3.5.7.3 自定义布局

    布局是什么 在系统中,布局决定了母版内的页面元素,一个页面可以由多个组件进行组装,布局可以根据视图类型来替换。 默认布局范围: 图3-5-7-36 默认布局范围 作用场景 系统内置了多个布局组件,这些组件适用于全局、某个应用或某个页面,提供了灵活的布局定制选项。这些组件根据不同类型的视图进行了默认的组装,这也是选择了视图类型后,页面能够呈现的原因。当然,这些默认的组装是可以被覆盖、新增和添加新组件的。 使用registerLayout进行自定义布局 开发者在使用这些布局组件时,应该遵循公司的规范进行统一的调整。自定义布局组件的使用可通过 registerLayout 实现。registerLayout 的第一个参数是代表布局的 XML,第二个参数是不同的选项维度,默认包含以下维度: viewType: 视图类型 module: 视图模型所在模块 moduleName: 视图模型所在模块名称 model: 视图模型编码 modelName: 视图模型名称 viewName: 视图名称 actionName: 动作名称 inline: 是否内嵌视图(子视图特有) ttype: 模型字段类型(子视图特有) relatedTtype: 关联模型字段类型(子视图特有) field: 字段(子视图特有) 需要注意的是,动作可以是A模块下的a模型,这个动作可以打开B模块下的b模型的视图,module、moduleName、model、modelName应该填b模型对应的值,只不过大部分场景我们都是本模型的动作打开本模型的视图,所以这些场景拿动作所在模型填这些值也可以这些纬度也可以通过查看TS的定义查看 全局 在系统中,我们可以通过指定视图类型来决定某一类视图的全局布局。以表格为例,当第二个入参为 { viewType: ViewType.Table } 时,代表了替换了系统内所有表格的布局样式。 示例工程目录 以下是需关注的工程目录示例,main.ts更新导入./layout,layout/index.ts更新导出./tableLayout: 图3-5-7-37 全局纬度注册布局工程目录示例 示例代码 import {registerLayout, ViewType} from '@kunlun/dependencies' /** * 把系统内所有表格类型视图的全局动作放入搜索区域 * * 移动actionBar布局至外层 * <element widget="actionBar" slot="actionBar" slotSupport="action"> * <xslot name="actions" slotSupport="action" /> * </element> * */ const registerGlobalTableLayout = () => { return registerLayout(`<view type="TABLE"> <element widget="actionBar" slot="actionBar" slotSupport="action"> <xslot name="actions" slotSupport="action" /> </element> <pack widget="group"> <view type="SEARCH"> <element widget="search" slot="search" slotSupport="field" /> </view> </pack> <pack widget="group" slot="tableGroup"> <element widget="table" slot="table" slotSupport="field"> <element widget="expandColumn" slot="expandRow" /> <xslot name="fields" slotSupport="field" /> <element widget="rowActions" slot="rowActions" slotSupport="action" /> </element> </pack> </view>`, { viewType: ViewType.Table }) } registerGlobalTableLayout() 图3-5-7-38 全局纬度注册布局代码示例 效果 图3-5-7-39 全局纬度注册布局效果示例 应用 在系统中,我们可以通过指定视图类型和模块名称来替换某一类视图在特定模块下的全局布局。以表格为例,当第二个入参为 { viewType: ViewType.Table, moduleName: ‘resource’ } 时,代表了替换了资源应用下所有表格的布局样式,而其他应用仍使用默认布局 import {registerLayout, ViewType} from '@kunlun/dependencies' const registerModuleTableLayout = () => { return registerLayout(`<view type="TABLE"> <element widget="actionBar" slot="actionBar" slotSupport="action"> <xslot name="actions" slotSupport="action" /> </element> <pack widget="group"> <view type="SEARCH"> <element widget="search" slot="search" slotSupport="field" /> </view> </pack> <pack…

    2024年5月23日
    00
  • 2.1 数字化时代软件业的另一个本质变化

    随着企业从信息化向数字化转变,软件公司提供的产品也由传统的企业管理软件向企业商业支撑软件发展。这一变化带来了许多技术上的挑战和机遇。在之前的章节中,我们提到企业的视角已经从内部管理转向业务在线和生态在线协同,这也带来了一系列新的需求。但是,我们常常会忽视这一变化所带来的对系统要求的变化。在本章中,我们将探讨这些技术上的变化,以及这些变化所带来的机遇和挑战。 图2-1 从信息化到数字化软件本质变化 在信息化时代,企业的业务围绕着内部管理效率展开,借鉴国外优秀的管理经验,企业将其管理流程固化下来,典型的例子是ERP项目。这类项目上线后往往长期稳定,不轻易更改,因此信息化时代软件的技术流派侧重于通过模型对业务进行全面支持。例如,SAP具有丰富的配置能力,将已有企业管理思想抽象到极致。其功能基本上可以通过配置来实现,因此其模型设计特别复杂。但是,我们也应该清楚地了解到,配置是面向已知问题的。在数字化时代,创新和业务迭代速度非常快,这种方法可能就不太适合了。我们知道,模型抽象是在设计时具有前瞻性的,一旦不适合,修改起来就会异常困难。 随着数字化时代的到来,企业主的关注点已经从单一企业内部管理转变为了围绕企业上下游价值链的协同展开。这种变化给企业信息化系统提出了更高的要求,例如业务需求的响应速度、系统性能和用户体验等方面。现在,企业对软件不仅是管理需求的承载,更是业务在线化的承载。传统的重模型设计软件模式已经不再适用,因为业务本身不断创新和变化。因此,数字化时代需要新的软件技术流派,这种流派必须是轻模型加上低代码技术的结合体。通过模型抽象80%的通用场景,剩余的20%个性化需求可以通过技术手段来完成。这样的设计可以让每家企业的研发人员轻松理解模型,而不像ERP模型那样异常复杂,无法进行修改。此外,配合低代码技术可以快速研发和上线。如果说配置化是面向已知问题的,那么低代码就是面向未知问题设计的。虽然低代码的概念可以追溯到上个世纪80年代,当时是为了满足企业内部部门之间有协同需求,但又没有专业软件支撑,定制化开发又不划算的辅助场景。但现在它的核心原因是企业数字化的核心场景不稳定,变化很快,每家企业都有强烈的个性化需求。因此,低代码成为解决这些问题的核心手段,数字化时代的低代码需要具备处理复杂场景的能力,而不仅仅是围绕着内部管理展开。 企业在数字化转型的过程中需要考虑到不仅是成熟的全链路业务解决方案,还要应对数字化场景的快速变化和持续创新的需求。为此,Oinone打造了一站式低代码商业支撑平台,从业务与技术两个维度来帮助企业建立开放、链接、安全的数字化平台。这将在水平和垂直两个维度上全面推动企业数字化转型。 另外,低代码的另一个好处是完成了软件本身的数字化建设。通过基于元数据设计,元数据成为软件中数据、逻辑和交互的数据,软件结合AI可以有更多的创造可能。想象一下,AI了解软件的元数据后可以自我运作,人在极少情况下才需要参与,人机交互也会发生大的改变。未来的软件交互不再需要研发提前预设,而是能够实现用户所需即所呈现的效果。作为一家帮助企业进行数字化转型的软件公司,请问您的数字化转型是否已经完成呢?

    2024年5月23日
    00
  • 4.1.12 函数之内置函数与表达式

    本文意在列全所有内置函数与表达式,方便大家查阅。 一、内置函数 内置函数是系统预先定义好的函数,并且提供表达式调用支持。 通用函数 数学函数 表达式 名称 说明 ABS 绝对值 函数场景: 表达式函数示例: ABS(number)函数说明: 获取number的绝对值 FLOOR 向下取整 函数场景: 表达式函数示例: FLOOR(number)函数说明: 对number向下取整 CEIL 向上取整 函数场景: 表达式函数示例: CEIL(number)函数说明: 对number向上取整 ROUND 四舍五入 函数场景: 表达式函数示例: ROUND(number)函数说明: 对number四舍五入 MOD 取余 函数场景: 表达式函数示例: MOD(A,B)函数说明: A对B取余 SQRT 平方根 函数场景: 表达式函数示例: SQRT(number) 函数说明: 对number平方根 SIN 正弦 函数场景: 表达式函数示例: SIN(number)函数说明: 对number取正弦 COS 余弦 函数场景: 表达式函数示例: COS(number)函数说明: 对number取余弦 PI 圆周率 函数场景: 表达式函数示例: PI() 函数说明: 圆周率 ADD 相加 函数场景: 表达式函数示例: ADD(A,B)函数说明: A与B相加 SUBTRACT 相减 函数场景: 表达式函数示例: SUBTRACT(A,B)函数说明: A与B相减 MULTIPLY 乘积 函数场景: 表达式函数示例: MULTIPLY(A,B)函数说明: A与B相乘 DIVIDE 相除 函数场景: 表达式函数示例: DIVIDE(A,B)函数说明: A与B相除 MAX 取最大值 函数场景: 表达式函数示例: MAX(collection) 函数说明: 返回集合中的最大值,参数collection为集合或数组 MIN 取最小值 函数场景: 表达式函数示例: MIN(collection) 函数说明: 返回集合中的最小值,参数collection为集合或数组 SUM 求和 函数场景: 表达式函数示例: SUM(collection)函数说明: 返回对集合的求和,参数collection为集合或数组 AVG 取平均值 函数场景: 表达式函数示例: AVG(collection)函数说明: 返回集合的平均值,参数collection为集合或数组 COUNT 计数 函数场景: 表达式函数示例: COUNT(collection)函数说明: 返回集合的总数,参数collection为集合或数组 UPPER_MONEY 大写金额 函数场景: 表达式函数示例: UPPER_MONEY(number)函数说明: 返回金额的大写,参数number为数值或数值类型的字符串 表4-1-12-1 数学函数 文本函数 表达式 名称 说明 TRIM 空字符串过滤 函数场景: 表达式函数示例: TRIM(text)函数说明: 去掉文本字符串text中的首尾空格,文本为空时,返回空字符串 IS_BLANK 是否为空字符串 函数场景: 表达式函数示例: IS_BLANK(text)函数说明: 判断文本字符串text是否为空 STARTS_WITH 是否以指定字符串开始 函数场景: 表达式函数示例: STARTS_WITH(text,start)函数说明: 判断文本字符串text是否以文本字符串start开始,文本为空时,按照空字符串处理 ENDS_WITH 是否以指定字符串结束 函数场景: 表达式函数示例: ENDS_WITH(text,start)函数说明: 判断文本字符串text是否以文本字符串end结束,文本为空时,按照空字符串处理 CONTAINS 包含 函数场景: 表达式函数示例: CONTAINS(text,subtext)函数说明: 判断文本字符串text是否包含文本字符串subtext,文本text为空时,按照空字符串处理 LOWER 小写 函数场景: 表达式函数示例: LOWER(text)函数说明: 小写文本字符串text,文本为空时,按照空字符串处理 UPPER 大写 函数场景: 表达式函数示例: UPPER(text)函数说明: 大写文本字符串text,文本为空时,按照空字符串处理 REPLACE 替换字符串 函数场景: 表达式函数示例: REPLACE(text,oldtext,newtext)函数说明: 使用文本字符串newtext替换文本字符串text中的文本字符串oldtext…

    Oinone 7天入门到精通 2024年5月23日
    00
  • 4.1.6 模型之元数据详解

    介绍Model相关元数据,以及对应代码注解方式。大家还是可以通读并练习每种不同的使用方式,这个是oinone的设计精华所在。当您不知道如何配置模型、字段、模型间的关系、以及枚举都可以到这里找到。 一、模型元数据 安装与更新 使用@Model.model来配置模型的不可变更编码。模型一旦安装,无法在对该模型编码值进行修改,之后的模型配置更新会依据该编码进行查找并更新;如果仍然修改该注解的配置值,则系统会将该模型识别为新模型,存储模型会创建新的数据库表,而原表将会rename为废弃表。 如果模型配置了@Base注解,表明在studio中该模型配置不可变更;如果字段配置了@Base注解,表明在studio中该字段配置不可变更。 注解配置 模型类必需使用@Model注解来标识当前类为模型类。 可以使用@Model.model、@Fun注解模型的模型编码(也表示命名空间),先取@Model.model注解值,若为空则取@Fun注解值,若皆为空则取全限定类名。 模型元信息 模型的priority,当展示模型定义列表时,使用priority配置来对模型进行排序。 模型的ordering,使用ordering属性来配置该模型的数据列表的默认排序。 模型元信息继承形式: 不继承(N) 同编码以子模型为准(C) 同编码以父模型为准(P) 父子需保持一致,子模型可缺省(P=C) 注意:模型上配置的索引和唯一索引不会继承,所以需要在子模型重新定义。数据表的表名、表备注和表编码最终以父模型配置为准;扩展继承父子模型字段编码一致时,数据表字段定义以父模型配置为准。 名称 描述 抽象继承 同表继承 代理继承 多表继承 基本信息 displayName 显示名称 N N N N summary 描述摘要 N N N N label 数据标题 N N N N check 模型校验方法 N N N N rule 模型校验表达式 N N N N 模型编码 model 模型编码 N N N N 高级特性 name 技术名称 N N N N table 逻辑数据表名 N P=C P=C N type 模型类型 N N N N chain 是否是链式模型 N N N N index 索引 N N N N unique 唯一索引 N N N N managed 需要数据管理器 N N N N priority 优先级,默认100 N N N N ordering 模型查询数据排序 N N N N relationship 是否是多对多关系模型 N N N N inherited 多重继承 N N N N unInheritedFields 不从父类继承的字段 N N N N unInheritedFunctions 不从父类继承的函数 N N N N 高级特性-数据源 dsKey 数据源 N P=C P=C N 高级特性-持久化 logicDelete 是否逻辑删除 P P P N logicDeleteColumn 逻辑删除字段 P P P N logicDeleteValue 逻辑删除状态值 P P P N logicNotDeleteValue 非逻辑删除状态值 P P P N underCamel 字段是否驼峰下划线映射 P P P N capitalMode 字段是否大小写映射…

    2024年5月23日
    00
  • 3.5.2.2 构建View的Template

    我们在很多时候需要自定义模型的管理页面,而不是直接使用默认页面,比如字段的展示与隐藏,Action是否在这个页面上出现,搜索条件自定义等等,那么本章节带您一起学习如何自定义View的Template。 自定义View的Template 在使用默认layout的情况下,我们来做几个自定义视图Template,并把文件放到指定目录下。 图3-5-2-14 自定义View的Template 第一个Tabel Step1 自定义PetTalent的列表 我们先通过数据库查看默认页面定义,找到base_view表,过滤条件设置为model =\’demo.PetTalent\’,我们就看到该模型下对应的所有view,这些是系统根据该模型的ViewAction对应生成的默认视图,找到类型为【表格(type = TABLE)】的记录,查看template字段。 图3-5-2-15 base_view表查看template字段 <view name="tableView" cols="1" type="TABLE" enableSequence="false"> <template slot="actions" autoFill="true"/> <template slot="rowActions" autoFill="true"/> <template slot="fields"> <field invisible="true" data="id" label="ID" readonly="true"/> <field data="name" label="达人"/> <field data="dataStatus" label="数据状态"> <options> <option name="DRAFT" displayName="草稿" value="DRAFT" state="ACTIVE"/> <option name="NOT_ENABLED" displayName="未启用" value="NOT_ENABLED" state="ACTIVE"/> <option name="ENABLED" displayName="已启用" value="ENABLED" state="ACTIVE"/> <option name="DISABLED" displayName="已禁用" value="DISABLED" state="ACTIVE"/> </options> </field> <field data="createDate" label="创建时间" readonly="true"/> <field data="writeDate" label="更新时间" readonly="true"/> <field data="createUid" label="创建人id"/> <field data="writeUid" label="更新人id"/> </template> <template slot="search" autoFill="true" cols="4"/> </view> 图3-5-2-16 base_view表查看template字段 对比view的template定义与页面差异,从页面上看跟view的定义少了,创建人id和更新人id。因为这两个字段元数据定义里invisible属性。 a. 当XML里面没有配置,则用元数据覆盖了。 b. 当XML里面配置了,则不会用元数据覆盖了。 在下一步中我们只要view的DSL中给这两个字段加上invisible="false"就可以展示出来了 图3-5-2-17 查看列表展示 图3-5-2-18 invisible属性 新建pet_talent_table.xml文件放到对应的pamirs/views/demo_core/template目录下,内容如下 a. 对比默认视图,在自定义视图时需要额外增加属性model="demo.PetTalent" b. name设置为"tableView",系统重启后会替换掉base_view表中model为"demo.PetTalent",name为"tableView",type为"TABLE"的数据记录。 ⅰ. name不同的但type相同,且viewAction没有指定时,根据优先级priority进行选择。小伙伴可以尝试修改name="tableView1",并设置priority为1,默认生成的优先级为10,越小越优先。 ccreateUid和writeUid字段,增加invisible="false"的属性定义 <view name="tableView" model="demo.PetTalent" cols="1" type="TABLE" enableSequence="false"> <template slot="actions" autoFill="true"/> <template slot="rowActions" autoFill="true"/> <template slot="fields"> <field invisible="true" data="id" label="ID" readonly="true"/> <field data="name" label="达人"/> <field data="dataStatus" label="数据状态"> <options> <option name="DRAFT" displayName="草稿" value="DRAFT" state="ACTIVE"/> <option name="NOT_ENABLED" displayName="未启用" value="NOT_ENABLED" state="ACTIVE"/> <option name="ENABLED" displayName="已启用" value="ENABLED" state="ACTIVE"/> <option name="DISABLED" displayName="已禁用" value="DISABLED" state="ACTIVE"/> </options> </field> <field data="createDate" label="创建时间" readonly="true"/> <field data="writeDate" label="更新时间" readonly="true"/> <field data="createUid" label="创建人id" invisible="false"/> <field data="writeUid" label="更新人id" invisible="false"/> </template> <template slot="search" autoFill="true" cols="4"/> </view> 图3-5-2-19 增加invisible=”false”的属性定义 Step2 重启应用看效果 图3-5-2-20 示例效果 第一个Form Step1 自定义PetTalent的编辑页…

    2024年5月23日
    00

Leave a Reply

登录后才能评论