feat: 简化技术描述语言(2.8)
This commit is contained in:
117
knowledge.md
117
knowledge.md
@@ -525,111 +525,118 @@ TOCO中判断使用方式的步骤:
|
|||||||
- 建议在Converter中扩展代码,不建议修改结构定义文件。VO的**自定义字段**不直接派生自DTO,一般对应取数逻辑代码。涉及数据获取、计算和拼装时,批量处理性能最好,自定义字段对应代码位置**必须**放在Converter的**Map**基础转换方法convertTo${VoName}Map中,批量取数组装,如UserVoConverter.convertToUserVoMap
|
- 建议在Converter中扩展代码,不建议修改结构定义文件。VO的**自定义字段**不直接派生自DTO,一般对应取数逻辑代码。涉及数据获取、计算和拼装时,批量处理性能最好,自定义字段对应代码位置**必须**放在Converter的**Map**基础转换方法convertTo${VoName}Map中,批量取数组装,如UserVoConverter.convertToUserVoMap
|
||||||
|
|
||||||
#### **2.8 查询对象(WO)**
|
#### **2.8 查询对象(WO)**
|
||||||
- **定义与用途:** 在TOCO中,WO表达某个Entity为基本,通过外键关系不断关联多个Entity的数据结构。WO还隐式表达了数据的取数拼装,这种拼装符合外键关系. WO作为ReadPlan的查询上下文使用,所以在创建ReadPlan之前需要先创建WO对象。在理解一个ReadPlan的语义的时候需要以WO作为上下文。
|
- **定义与用途:** WO基于Entity通过外键关系关联多个Entity的数据结构。WO表达数据取数拼装逻辑,符合外键关系。WO作为ReadPlan查询上下文,创建ReadPlan前需先创建WO对象。理解ReadPlan语义时需要WO作为上下文。
|
||||||
如果对应的需求,在返回DTO|VO的时候需要对DTO|VO的列表属性进行过滤,则需要根据对应的DTO|VO的结构定义,扩展出对应的WO对象(需要过滤的字段名需要和DTO|VO的字段名保持一致),例如:
|
当返回DTO|VO时需要对列表属性进行过滤,则根据DTO|VO结构定义,扩展对应WO对象(过滤字段名需与DTO|VO字段名一致)。例如:
|
||||||
查询DTO
|
查询DTO
|
||||||
<code>
|
```java
|
||||||
class MeetingDto {
|
class MeetingDto {
|
||||||
String meetingId;
|
String meetingId;
|
||||||
String meetingName;
|
String meetingName;
|
||||||
List<MeetingAgenda> agendaList;
|
List<MeetingAgenda> agendaList;
|
||||||
}
|
}
|
||||||
</code>
|
```
|
||||||
需求是需要根据会议名称查询会议列表,并且根据会议议程信息过滤掉部分议程,那在定义查询对象的时候就需要包含议程信息,并且扩展字段的名称要定义为**agendaList**
|
需求:根据会议名称查询会议列表,根据议程信息过滤部分议程。定义查询对象时需包含议程信息,扩展字段名定义为**agendaList**
|
||||||
- **查询对象设计元素的表达**
|
|
||||||
- 以json格式表达,json schema 定义如下
|
- **查询对象设计元素表达**
|
||||||
|
以json格式表达,json schema如下:
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"type": "object","description": "查询对象定义", "required": ["dtoOrVoId","name","fromEntity"],
|
"type": "object","description": "查询对象定义", "required": ["dtoOrVoId","name","fromEntity"],
|
||||||
"properties": {
|
"properties": {
|
||||||
"name": {"type": "string", "description": "查询对象名称,用英语表达,单词之间下划线分割,长度补超过32个字符"},
|
"name": {"type": "string", "description": "查询对象名称,英语表达,下划线分割,不超过32字符"},
|
||||||
"uuid": {"type": "string", "description": "查询对象uuid, 在更新的时候必须传递(只有根节点必须传递),创建的时候不传递"},
|
"uuid": {"type": "string", "description": "查询对象uuid,更新时必传(只有根节点),创建时不传"},
|
||||||
"dtoOrVoId":{"type":"string","description":"返回数据对象(VO或DTO)的uuid,创建的时候必须指定,更新的时候不传递"},
|
"dtoOrVoId":{"type":"string","description":"返回数据对象(VO或DTO)的uuid,创建时必填,更新时不传"},
|
||||||
"moduleName": {"type": "string", "description": "查询对象所属模块名称,在创建的时候必须传递"},
|
"moduleName": {"type": "string", "description": "查询对象所属模块名称,创建时必传"},
|
||||||
"fromEntity": {"type": "string", "description": "查询对象对应的实体"},
|
"fromEntity": {"type": "string", "description": "查询对象对应的实体"},
|
||||||
"expandList": {
|
"expandList": {
|
||||||
"type": "array", "description": "正向扩展列表",
|
"type": "array", "description": "正向扩展列表",
|
||||||
"items":
|
"items": {
|
||||||
{
|
|
||||||
"type": "object","description": "正向扩展定义","required": ["field","wo","fieldName"],
|
"type": "object","description": "正向扩展定义","required": ["field","wo","fieldName"],
|
||||||
"properties": {
|
"properties": {
|
||||||
"field": {"type": "string", "description": "指定fromEntity的扩展字段,必须是fromEntity的外键字段"},
|
"field": {"type": "string", "description": "fromEntity扩展字段,必须是fromEntity外键字段"},
|
||||||
"wo": {"$ref": "#"},
|
"wo": {"$ref": "#"},
|
||||||
"fieldName": {"type": "string", "description": "扩展出来的字段名称"}
|
"fieldName": {"type": "string", "description": "扩展字段名称"}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"reverseExpandList": {
|
"reverseExpandList": {
|
||||||
"type": "array", "description": "反向扩展列表",
|
"type": "array", "description": "反向扩展列表",
|
||||||
"items":
|
"items": {
|
||||||
{
|
|
||||||
"type": "object","description": "反向扩展定义","required": ["field","wo","fieldName"],
|
"type": "object","description": "反向扩展定义","required": ["field","wo","fieldName"],
|
||||||
"properties": {
|
"properties": {
|
||||||
"field": {"type": "string", "description": "指定fromEntity的扩展字段,必须是fromEntity的外键字段"},
|
"field": {"type": "string", "description": "fromEntity扩展字段,必须是fromEntity外键字段"},
|
||||||
"wo": {"$ref": "#"},
|
"wo": {"$ref": "#"},
|
||||||
"fieldName": {"type": "string", "description": "扩展出来的字段名称"}
|
"fieldName": {"type": "string", "description": "扩展字段名称"}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
- **如何创建/生成:**
|
|
||||||
- **创建思路** 按照需要查询返回的DTO|VO的结构,构建出同构的WO对象(扩展和反向扩展的字段名保持一致),然后根据查询需求和过滤需求对WO进行二次裁剪或和扩展:
|
- **创建/生成方式:**
|
||||||
|
- **创建思路** 按查询返回的DTO|VO结构,构建同构WO对象(扩展和反向扩展字段名保持一致),根据查询需求和过滤需求对WO二次裁剪或扩展:
|
||||||
- 去掉过滤和查询都不需要的扩展
|
- 去掉过滤和查询都不需要的扩展
|
||||||
- 补全查询或者字段过滤需要扩展
|
- 补全查询或字段过滤需要的扩展
|
||||||
- **关键配置:** Wo中的字段分为三种:a.继承Entity的字段,和Entity的字段类型一样;b.扩展字段,含正向替换和反向注入字段,类型为WO或List<WO>;
|
- **关键配置:** WO字段分三种:a.继承Entity字段,类型与Entity相同;b.扩展字段,含正向替换和反向注入字段,类型为WO或List<WO>
|
||||||
- **字段扩展方式:** TOCO定义了一个WO组装方法,适用于WO通过外键关系替换/注入对应Entity的信息,对象化表达有外键关系的Entity信息。只要存在外键关系且满足以下条件即可扩展:a.对于正向替换:当前实体存在指向其他实体的外键字段;b.对于反向注入:其他实体存在指向当前实体的外键字段。
|
- **字段扩展方式:** WO通过外键关系替换/注入对应Entity信息,对象化表达有外键关系的Entity信息。存在外键关系且满足条件即可扩展:a.正向替换:当前实体有指向其他实体的外键字段;b.反向注入:其他实体有指向当前实体的外键字段。
|
||||||
例如:有两个Entity
|
|
||||||
<code>
|
例如:两个Entity
|
||||||
|
```java
|
||||||
MeetingRoom{ //会议室
|
MeetingRoom{ //会议室
|
||||||
Long id;// 会议室id,主键
|
Long id;// 会议室id,主键
|
||||||
String name;// 会议室名称
|
String name;// 会议室名称
|
||||||
}
|
}
|
||||||
Meeting { //会议
|
Meeting { //会议
|
||||||
Long id;// 会议id,主键
|
Long id;// 会议id,主键
|
||||||
Long roomid; //占用的会议室id,到MeetingRoom的外键,n:1关系
|
Long roomid; //占用会议室id,到MeetingRoom外键,n:1关系
|
||||||
Long backupRoomid; //备用的会议室id,到MeetingRoom的外键,n:1关系
|
Long backupRoomid; //备用会议室id,到MeetingRoom外键,n:1关系
|
||||||
String title; //会议标题
|
String title; //会议标题
|
||||||
DateTime startTime; //会议开始时间
|
DateTime startTime; //会议开始时间
|
||||||
DateTime endTime; //会议结束时间
|
DateTime endTime; //会议结束时间
|
||||||
}
|
}
|
||||||
</code>
|
```
|
||||||
其中Meeting和MeetingRoom是n:1关系。即多个会议室会占用同一个会议室。
|
|
||||||
当组装对象以某Entity为根时,那么首先它将拥有和该Entity一样的数据结构,并将通过下面的“正向替换”,“反向注入”的行为,递归的将多个互相之间有外键关系的Entity的信息组装到该组装对象中。
|
Meeting和MeetingRoom是n:1关系。多个会议占用同一个会议室。
|
||||||
定义“正向替换”这个行为,选定一个表,这张表存在到另外一张表的一个或多个外键。选择和需求相关的具体的外键属性,将该外键属性替换为另一张表为根的组装对象。这样就可以获取基于某些外键且包含另一张表更详细的属性数据。
|
组装对象以某Entity为根时,首先拥有与该Entity相同数据结构,通过"正向替换"、"反向注入"行为,递归组装有外键关系的Entity信息。
|
||||||
例如:需要会议和其占用会议室时,将Meeting表中的roomid外键替换为以MeetingRoom为根的组装对象,而backupRoomid对应的候选会议室具体信息和本需求无关,不做任何替换。如下:
|
|
||||||
<code>
|
**正向替换**:选定表,该表有到另一表的外键。选择需求相关的外键属性,将外键属性替换为另一表为根的组装对象。获取基于外键且包含另一表详细属性的数据。
|
||||||
|
例如:需要会议和占用会议室时,将Meeting表中roomid外键替换为以MeetingRoom为根的组装对象,backupRoomid不替换:
|
||||||
|
|
||||||
|
```
|
||||||
MeetingWithRoomWo {
|
MeetingWithRoomWo {
|
||||||
Long id;// 会议id
|
Long id;// 会议id
|
||||||
MeetingRoomWo room { //正向替换该会议用的会议室信息,是一个以会议室为根的对象
|
MeetingRoomWo room { //正向替换会议用的会议室信息,以会议室为根的对象
|
||||||
Long id; //会议室ID
|
Long id; //会议室ID
|
||||||
String name;// 会议室名称
|
String name;// 会议室名称
|
||||||
}
|
}
|
||||||
Long backupRoomid; // 不做变化
|
Long backupRoomid; // 不变化
|
||||||
String title; //会议标题
|
String title; //会议标题
|
||||||
DateTime startTime; //会议开始时间
|
DateTime startTime; //会议开始时间
|
||||||
DateTime endTime; //会议结束时间
|
DateTime endTime; //会议结束时间
|
||||||
}
|
}
|
||||||
</code>
|
```
|
||||||
又例如:需要会议和其候选会议室时,将Meeting表中的backupRoomid进行正向替换。
|
|
||||||
又例如:需要会议,且即需要其占用会议室,也需要候选会议室时,将Meeting表中的roomid,backupRoomid都进行正向替换。
|
**反向注入**:选定表,其他表到该表有外键,选择需求相关的外键属性在选定表中增加以另一表为根的组合对象(1:1关系时)或组合对象列表(n:1关系时)。
|
||||||
同时TOCO还定义了“反向注入”这个行为,选定一个表,如果有另外的表到前表有外键,选择和需求相关的具体的外键属性在选定表中增加一个以另外表为根的组合对象(当外键关系是1:1时)或者组合对象的列表(当外键关系是n:1时)。需求是:“获取会议室,和占用它的会议信息”,需要选定MeetingRoom,那么基于另外的表Meeting中存在字段roomid,为到MeetingRoom的n:1关系外键。可以将List<MeetingBaseDto>反向注入到MeetingRoom中,最终生成一个以Meeting为根的组装对象,生成:
|
需求:"获取会议室和占用它的会议信息",选定MeetingRoom,基于Meeting表存在roomid字段为到MeetingRoom的n:1关系外键。将List<MeetingBaseDto>反向注入到MeetingRoom中:
|
||||||
<code>
|
|
||||||
|
```
|
||||||
MeetingRoomWithMeetingWo {
|
MeetingRoomWithMeetingWo {
|
||||||
Long id;// 会议室id
|
Long id;// 会议室id
|
||||||
String name;// 会议室名称
|
String name;// 会议室名称
|
||||||
List<MeetingBaseWo> meetingList { //反向注入的用该会议室的会议信息,是以会议为根的对象
|
List<MeetingBaseWo> meetingList { //反向注入使用该会议室的会议信息,以会议为根的对象
|
||||||
Long id;// 会议id
|
Long id;// 会议id
|
||||||
String title; //会议标题
|
String title; //会议标题
|
||||||
DateTime startTime; //会议开始时间
|
DateTime startTime; //会议开始时间
|
||||||
DateTime endTime; //会议结束时间
|
DateTime endTime; //会议结束时间
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</code>
|
```
|
||||||
这种“正向替换”和“反向注入”可以按需递归调用,去将多个互相之间有外键关系的对象组装成最终对象。例如,还有另外一张表MeetingAgenda到Meeting有n:1的外键,那么如果我要去组装以Meeting开始,包含MeetingRoom, MeetingAgenda的查询对象,首先发现MeetingRoom是可以正向扩展到Meeting的,并且可以反向注入MeetingAgenda
|
|
||||||
- meeting_with_room_and_agenda_wo
|
"正向替换"和"反向注入"可按需递归调用,组装多个有外键关系的对象。例如,MeetingAgenda到Meeting有n:1外键,组装以Meeting开始,包含MeetingRoom、MeetingAgenda的查询对象,MeetingRoom可正向扩展到Meeting,可反向注入MeetingAgenda。
|
||||||
<code>
|
|
||||||
|
meeting_with_room_and_agenda_wo示例:
|
||||||
|
```json
|
||||||
{
|
{
|
||||||
"wo": {
|
"wo": {
|
||||||
"uuid": null,
|
"uuid": null,
|
||||||
@@ -661,7 +668,8 @@ TOCO中判断使用方式的步骤:
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</code>
|
```
|
||||||
|
|
||||||
#### **2.9 读方案 (ReadPlan)**
|
#### **2.9 读方案 (ReadPlan)**
|
||||||
- **定义与用途:** 在TOCO中,针对DTO和VO,读方案描述了如何基于查询对象从数据库获取DTO和VO列表数据,主要提供了三个能力
|
- **定义与用途:** 在TOCO中,针对DTO和VO,读方案描述了如何基于查询对象从数据库获取DTO和VO列表数据,主要提供了三个能力
|
||||||
- 根据查询条件,返回符合条件的DTO或VO的id列表
|
- 根据查询条件,返回符合条件的DTO或VO的id列表
|
||||||
@@ -954,11 +962,13 @@ TOCO中判断使用方式的步骤:
|
|||||||
* 根据用户名称查询用户列表返回UserDTO,则生成UserNameQto、UserNameQtoService、UserNameQtoDao、UserNameQueryService; UserNameQueryService调用UserNameQtoService,UserNameQtoService调用UserNameQtoDao
|
* 根据用户名称查询用户列表返回UserDTO,则生成UserNameQto、UserNameQtoService、UserNameQtoDao、UserNameQueryService; UserNameQueryService调用UserNameQtoService,UserNameQtoService调用UserNameQtoDao
|
||||||
- **修改建议:**
|
- **修改建议:**
|
||||||
- 如果有对结果的数据二次处理,建议在QueryService和QueryExecutor中进行代码扩展,不建议修改QTO文件
|
- 如果有对结果的数据二次处理,建议在QueryService和QueryExecutor中进行代码扩展,不建议修改QTO文件
|
||||||
|
|
||||||
#### **2.10 查询传输对象(QTO)**
|
#### **2.10 查询传输对象(QTO)**
|
||||||
- **定义与用途:** 在TOCO中,QTO为读方案的查询参数结构,每个读方案会对应一个QTO,读方案调用方按照QTO的结构向读方案生成的RPC方法传入需要查询的实体字段值,完成对数据库的查询
|
- **定义与用途:** 在TOCO中,QTO为读方案的查询参数结构,每个读方案会对应一个QTO,读方案调用方按照QTO的结构向读方案生成的RPC方法传入需要查询的实体字段值,完成对数据库的查询
|
||||||
- **如何创建/生成:** 在创建读方案后,TOCO会自动生成QTO作为该读方案传入的查询参数结构,无需单独创建
|
- **如何创建/生成:** 在创建读方案后,TOCO会自动生成QTO作为该读方案传入的查询参数结构,无需单独创建
|
||||||
- **关键配置:** 名称(${ReadPlanNameQto},驼峰展示),查询字段列表(如idIs,nameLike, schoolNameLike等)
|
- **关键配置:** 名称(${ReadPlanNameQto},驼峰展示),查询字段列表(如idIs,nameLike, schoolNameLike等)
|
||||||
- **与API的关系:** QTO通常可作为API的参数,API接收到参数后可直接透传给内部的RPC进行调用,注意QTO只用作读操作的参数,**禁止用作写参数结构**
|
- **与API的关系:** QTO通常可作为API的参数,API接收到参数后可直接透传给内部的RPC进行调用,注意QTO只用作读操作的参数,**禁止用作写参数结构**
|
||||||
|
|
||||||
#### **2.11 写方案 (WritePlan,单聚合操作)**
|
#### **2.11 写方案 (WritePlan,单聚合操作)**
|
||||||
- **定义与用途:** TOCO针对写场景定义了一种写方案,所有对数据库的写操作都只能通过写方案实现(一个写方案只可以变更一个聚合的数据,无法同时操作多个聚合!)。写方案包含了对数据库表的写操作。每个写方案只能操作一个聚合内部的表,同时对一个聚合内表的操作尽量合并至一个写方案中(根据复用性、内聚性等方面具体情况具体分析)。注意写方案可以一次操作聚合内的多张表,如location和storey是同一个聚合,并且location和storey是**1:N**关系,location是父对象(聚合根),storey是子对象,那么我们可以创建一个写方案,同时更新单个聚合根location中的信息,以及操作其下的子表storey**列表**信息,如新增、删除、修改storey等;也可以创建一个写方案单独更新单个子表storey对象信息。
|
- **定义与用途:** TOCO针对写场景定义了一种写方案,所有对数据库的写操作都只能通过写方案实现(一个写方案只可以变更一个聚合的数据,无法同时操作多个聚合!)。写方案包含了对数据库表的写操作。每个写方案只能操作一个聚合内部的表,同时对一个聚合内表的操作尽量合并至一个写方案中(根据复用性、内聚性等方面具体情况具体分析)。注意写方案可以一次操作聚合内的多张表,如location和storey是同一个聚合,并且location和storey是**1:N**关系,location是父对象(聚合根),storey是子对象,那么我们可以创建一个写方案,同时更新单个聚合根location中的信息,以及操作其下的子表storey**列表**信息,如新增、删除、修改storey等;也可以创建一个写方案单独更新单个子表storey对象信息。
|
||||||
- **关键配置:** 名称(小写字母+下划线,不要以write_plan结尾,全局唯一),操作的聚合,聚合内的实体和字段,对每个实体的操作类型:CREATE(创建**单个**实体),UPDATE(更新**单个**实体), DELETE(删除**单个**实体), CREATE_ON_DUPLICATE_UPDATE(创建或者更新**单个**实体),FULL_MERGE( 批量更新**列表**数据,根据传入的列表数据,一条一条执行CREATE_ON_DUPLICATE_UPDATE的操作;并且删除掉老的列表中不在传入列表数据中的部分), PARTIAL_MERGE(批量更新**列表**数据,根据传入的列表数据,一条一条执行)
|
- **关键配置:** 名称(小写字母+下划线,不要以write_plan结尾,全局唯一),操作的聚合,聚合内的实体和字段,对每个实体的操作类型:CREATE(创建**单个**实体),UPDATE(更新**单个**实体), DELETE(删除**单个**实体), CREATE_ON_DUPLICATE_UPDATE(创建或者更新**单个**实体),FULL_MERGE( 批量更新**列表**数据,根据传入的列表数据,一条一条执行CREATE_ON_DUPLICATE_UPDATE的操作;并且删除掉老的列表中不在传入列表数据中的部分), PARTIAL_MERGE(批量更新**列表**数据,根据传入的列表数据,一条一条执行)
|
||||||
@@ -1385,12 +1395,15 @@ TOCO中判断使用方式的步骤:
|
|||||||
- **例子:**
|
- **例子:**
|
||||||
- 用户登录,在UserFlowService中生成一个函数invokeLoginFlow,该函数通过流程框架根据流程定义调用LoginNode,LoginNode中封装了用户登录的逻辑,LoginFlowContext中封装了用户登录的参数和结果。
|
- 用户登录,在UserFlowService中生成一个函数invokeLoginFlow,该函数通过流程框架根据流程定义调用LoginNode,LoginNode中封装了用户登录的逻辑,LoginFlowContext中封装了用户登录的参数和结果。
|
||||||
- **修改建议:** 不修改 service 中的函数, 不修改FlowConfig, 可以修改FlowContext, 添加/修改出入参数, 修改FlowNode中的具体业务逻辑。
|
- **修改建议:** 不修改 service 中的函数, 不修改FlowConfig, 可以修改FlowContext, 添加/修改出入参数, 修改FlowNode中的具体业务逻辑。
|
||||||
#### **2.16 自定义查询**
|
|
||||||
|
|
||||||
|
#### **2.12 自定义查询**
|
||||||
- 在读方案无法满足需求的情况下,可以使用自定义查询
|
- 在读方案无法满足需求的情况下,可以使用自定义查询
|
||||||
- 自定查询使用复杂的sql实现业务功能
|
- 自定查询使用复杂的sql实现业务功能
|
||||||
- 自定义查询的数据访问层使用MyBatis、MyBatisPlus实现
|
- 自定义查询的数据访问层使用MyBatis、MyBatisPlus实现
|
||||||
- 自定查询的时候框架不自动生成任何代码(需要模型编写全部代码)
|
- 自定查询的时候框架不自动生成任何代码(需要模型编写全部代码)
|
||||||
- 各层的代码位置严格遵守章节:**3.2 项目结构与导航**,必须有mapper层、service层、必须有DO对象、必须有DTO对象,如果是API返回数据,必须有VO对象
|
- 各层的代码位置严格遵守章节:**3.2 项目结构与导航**,必须有mapper层、service层、必须有DO对象、必须有DTO对象,如果是API返回数据,必须有VO对象
|
||||||
|
|
||||||
### **3 生成代码产物补充说明**
|
### **3 生成代码产物补充说明**
|
||||||
- **3.1 支持的语言/框架**
|
- **3.1 支持的语言/框架**
|
||||||
Java、SpringBoot、MyBatis-plus(读)、Hibernate(写)
|
Java、SpringBoot、MyBatis-plus(读)、Hibernate(写)
|
||||||
|
|||||||
Reference in New Issue
Block a user