diff --git a/knowledge.md b/knowledge.md index c2cdd3d..588240d 100644 --- a/knowledge.md +++ b/knowledge.md @@ -671,45 +671,45 @@ meeting_with_room_and_agenda_wo示例: ``` #### **2.9 读方案 (ReadPlan)** -- **定义与用途:** 在TOCO中,针对DTO和VO,读方案描述了如何基于查询对象从数据库获取DTO和VO列表数据,主要提供了三个能力 - - 根据查询条件,返回符合条件的DTO或VO的id列表 - - 根据查询条件,返回符合条件的DTO或VO的id数量 - - 根据字段过滤条件,对DTO和VO的列表字段数据进行过滤 +- **定义与用途:** 读方案描述如何从数据库获取DTO和VO列表数据,提供三个能力: + - 根据查询条件返回符合条件的DTO或VO的id列表 + - 根据查询条件返回符合条件的DTO或VO的id数量 + - 根据字段过滤条件对DTO和VO的列表字段数据进行过滤 - **注意** 不派生自DTO的VO不能创建读方案 -- 读方案能力边界 - - 读方案的的查询能力是sql的简化,实现了部分sql的查询能力,仅支持sql的 count、exists、left join, not、in、like、between、and、or、not 以及 ( ) , 以及排序 order by, 以及分页 limit ,offset, 不能使用group by, having 语法,不支持使用其他函数 - - 在使用exists 和 left join的时候只能通过外键扩展,其中如果外键是1:N的时候,只能使用exist, 如果是1:1的时候则可以使用 left join 和 exists. - 例如: 实体t 和他t1 ,t1存在列t_id为指向t的外键,如果外键关系为1:N,则 select * from t where exists (select * from t1 where t.id=t1.id) 合法; elect * from t left join t1 on t.id=t1.id 非法 - - 在过滤字段的选择上,在使用exists的时候,在条件中只能选择后表的字段, 在使用 left join的时候,则可以使用前表和后表字段 - 例如: 实体 t (id,name) ,实体 t1 (id,name,t_id) ,t1存在列t_id为指向t的外键, select * from t where exists (select * from t1 where t.id=t1.id and t.name like ?) 非法, select * from t left join t1 on t.id=t1.id and t1.name like ? 合法 - select * from t left join t1 on t.id=t1.id where t.name like ? 合法, select * from t left join t1 on t.id=t1.id where t1.name like ? 合法 -- 在选择使用度方案的时候**必须**符合读方案能力边界,如果需求在读方案的能力之外,必须使用 ** [2.16 自定义查询] ** 实现功能 -- **特别注意** 对于根据主键,获取单个或者批量获取DTO或者VO,并且不需要对DTO、VO的列表字段数据进行过滤的需求,必须使用**DTO自动生成的预定义方法**,禁止使用读方案 -- **关键配置:** 名称(小写字母+下划线,不要以read_plan结尾,全局唯一)、返回结构(DTO/VO,一个读方案**不能**同时返回多种DTO或VO)、查询条件的自然语言描述、是否生成计数方法、排序字段(如果选择不分页,则不需要)、过滤字段以及过滤条件。 -- **与RPC、代码的关系:** 对于每一个返回DTO的读方案,TOCO会为每种分页方式自动生成一个RPC方法,其参数为对应的QTO,返回值为DTO列表;如果选择了生成计数方法,则还会在生成一个RPC,参数为QTO,返回值为符合条件的DTO数量。同样,对于每一个返回VO的读方案,TOCO会自动生成一个Java方法,其参数为对应的QTO,返回值为VO列表,方法内部逻辑已经由TOCO完全实现;如果选择了生成计数方法,则还会生成一个count方法,参数为QTO,返回值为符合条件的DTO数量,方法内部逻辑已经由TOCO完全实现 -- **生成的读方案RPC** 的使用: 读方案生成的RPC属于该写方案所属的模块,其他模块如果需要调用该读方案,需要先订阅该写方案RPC,在生成代码后使用adapter调用该读服务;当前模块则可以直接调用该读方案对应的Service -- **如何创建/生成:** 在创建读方案时,必须先调用工具创建或选择现有的**一种**DTO或VO作为返回值类型,然后再定义查询条件的自然语言描述(如:根据用户姓名、年龄、学校名称,查询用户列表),分页方式、是否生成计数方法、排序字段等。 -- **排序** 对于返回列表数据的排序,排序规则和SQL的排序类似(通过指定字段以及升序|降序,字段可以组合;ReadPlan支持有**两种**方式: - - **默认排序**:指定默认排序字段(不需要入参指定排序字段) - - **自定义排序**:指定排序字段(需要入参指定排序字段),这种方式通常能很好的满足列表头动态指定排序的需求 - - **排序字段的来源**:并不是查询字段中的所有字段都能作为排序字段,因为返回的根DTO|VO的id去重列表,所以排序字段只能来源自根WO(不包括列表WO属性: 比如MeetingWO包含了List agendaList属性, 那么agendaList不能作为排序字段),以及根WO扩展出来的非列表属性的WO的字段(比如MeetingWO包含了MeetingRoomWO,那么MeetingRoomWO的属性也能作为排序字段),**注意**:为了唯一确定是给按照那个字段进行排序,排序字段是一个从根节点到当前属性的路径,例如:ADto包含了BDto bDto,BDto具备属性name,如果指定对BDto的name字段排序,则字段路径为:bDto.name -- 你需要提取需求中的查询部分信息,以输入的查询对象作为查询上下文件,构建一个查询语句 -- 如果有列表属性过滤需求(如果没有filter,对每个列表属性都是放回全部数据,例如:MeetingDto的MeetingAgendaDtoList属性,如果不设置MeetingAgendaDtoList的filter,则返回该会议的全部议程信息):你需要提取需求中的过滤部分信息,以输入的查询对象作为查询上下文件,针对可过滤字段(列表属性)分别创建过滤条件, **注意** 过滤条件不能使用列表属性作为查询条件属性(即不能使用contains语法),为了唯一确定是给那个列表属性的指定过滤条件,属性字段是一个从根节点属性到当前属性的路径,例如:ADto包含了List bDtoList;BDto包含了List cDtoList,如果对cDtoList使用Filter,则字段路径为:bDtoList.cDtoList -- 查询语法和过滤语法 - - 基本语法格式是 属性名 操作符 变量或常量,变量表示了这个数据是外部传入的,用 #变量名为格式;如果是枚举类型常量,需要使用'符号包起来,例如: 男性枚举类型 'MALE' - - 对于数值、时间类型属性有:!=, ==, >, <,<=,>=, in,notIn, isNullOrNot 这些操作符 - - 对于文本类型属性有:like, isNotNull, isNull,!=, ==, in,notIn, isNullOrNot 这些操作符 - - isNullOrNot操作符是一个三元操作符,用于根据入参判断过滤某个值是否为null。举例:user对象有gender字段,如果定义了语句 gender isNullOrNot #genderIsNullOrNot, 如果入参 genderIsNullOrNot为true,等价于条件 where gender is null; 如果入参#genderIsNullOrNot为false,等价于 where gender is not null - - 对于对象属性或者列表对象属性可以用isNull, isNotNull, isNullOrNot 这些操作符。注意本对象不能直接用这个操作符。 - - 对于对象属性还可以对其子属性进行上述查询 +- **读方案能力边界** + - 支持sql的count、exists、left join、not、in、like、between、and、or、括号、order by、limit、offset + - 不支持group by、having语法,不支持其他函数 + - 1:N外键关系只能使用exists,1:1外键关系可使用left join和exists + 例如:实体t和t1,t1有t_id外键指向t,若为1:N关系,则`select * from t where exists (select * from t1 where t.id=t1.id)`合法,`select * from t left join t1 on t.id=t1.id`非法 + - exists只能选择后表字段,left join可使用前表和后表字段 + 例如:实体t(id,name),实体t1(id,name,t_id),`select * from t where exists (select * from t1 where t.id=t1.id and t.name like ?)`非法,`select * from t left join t1 on t.id=t1.id and t1.name like ?`合法 +- 需求超出读方案能力时,必须使用自定义查询 +- **特别注意** 根据主键获取单个或批量DTO/VO且不需过滤列表字段数据的需求,必须使用DTO自动生成的预定义方法,禁止使用读方案 +- **关键配置:** 名称(小写字母+下划线,不以read_plan结尾,全局唯一)、返回结构(DTO/VO,一个读方案只能返回一种类型)、查询条件自然语言描述、是否生成计数方法、排序字段、过滤字段及过滤条件 +- **与RPC、代码的关系:** 返回DTO的读方案自动生成RPC方法,参数为QTO,返回值为DTO列表;返回VO的读方案自动生成Java方法,参数为QTO,返回值为VO列表,方法内部逻辑由TOCO完全实现 +- **生成的读方案RPC使用:** 读方案RPC属于对应模块,其他模块需先订阅该RPC,用adapter调用;当前模块直接调用对应Service +- **创建方式:** 先创建或选择一种DTO或VO作为返回值类型,然后定义查询条件自然语言描述、分页方式、是否生成计数方法、排序字段等 +- **排序** 支持两种方式: + - **默认排序**:指定默认排序字段(不需入参指定排序字段) + - **自定义排序**:指定排序字段(需入参指定排序字段),满足列表头动态排序需求 + - **排序字段来源**:只能来源自根WO(不包括列表WO属性),以及根WO扩展出的非列表属性的WO字段。排序字段是从根节点到当前属性的路径,例如:ADto包含BDto bDto,BDto有name属性,排序字段路径为:bDto.name +- 提取需求中的查询信息,以输入的查询对象作为上下文,构建查询语句 +- 如有列表属性过滤需求:提取需求中的过滤信息,针对可过滤字段(列表属性)分别创建过滤条件。过滤条件不能使用列表属性作为查询条件属性(不能使用contains语法)。属性字段是从根节点属性到当前属性的路径 +- **查询语法和过滤语法** + - 基本语法格式:属性名 操作符 变量或常量,变量格式为#变量名;枚举类型常量用单引号包围,例如:'MALE' + - 数值、时间类型属性操作符:!=, ==, >, <, <=, >=, in, notIn, isNullOrNot + - 文本类型属性操作符:like, isNotNull, isNull, !=, ==, in, notIn, isNullOrNot + - isNullOrNot是三元操作符,根据入参判断过滤某值是否为null。例如:gender isNullOrNot #genderIsNullOrNot,若入参为true等价于where gender is null,若为false等价于where gender is not null + - 对象属性或列表对象属性操作符:isNull, isNotNull, isNullOrNot。本对象不能直接用此操作符 + - 对象属性可对其子属性进行上述查询 - 查询变量不能作为条件属性 - - 查询条件之间可以使用AND, OR 进行连接 - - 可以插入括号()对条件进行分组 - - 对于列表对象属性只能使用contains、isNull、isNotNull操作符:如果是wo列表类型可以使用contains(子查询, 子查询的条件属性只能作用在对应的列表类型对象的属性上),表示了该列表属性中需要包含至少一个满足子查询条件的对象; 其他列表类型只能使用isNull或者isNotNull - - 把上述子查询通过and, or, not这三个连接符拼装在一起就可以完成一个查询。请不要使用没提示过的操作符号,连接符。 - - 查询条件中的入参可以在运行时传入或者不传入值,如果不传入值表示该参数相关的条件不起作用;基于这种查询语句实际运行时的动态效果,多个条件联合查询的时候可以优先使用AND - - 使用点号(.)可以访问当前对象的单值对象类型的子属性, 可以多个点号的组合访问嵌套单值对象的属性 - - 查询条件中的属性必须是当前查询对象的属性或单值对象属性或者单值对象的子属性 + - 查询条件间可用AND, OR连接 + - 可插入括号()对条件分组 + - 列表对象属性只能使用contains、isNull、isNotNull操作符:wo列表类型可用contains(子查询),表示列表属性中需包含至少一个满足子查询条件的对象;其他列表类型只能用isNull或isNotNull + - 通过and, or, not连接符拼装子查询完成查询。不要使用未提及的操作符号、连接符 + - 查询条件中的入参可在运行时传入或不传入值,不传入值表示该参数相关条件不起作用;基于这种动态效果,多条件联合查询可优先使用AND + - 使用点号(.)访问当前对象的单值对象类型子属性,可多个点号组合访问嵌套单值对象属性 + - 查询条件中的属性必须是当前查询对象的属性或单值对象属性或单值对象子属性 - 禁止使用filter语法 - 禁止使用has语法 - 语法定义:使用 lezer 定义了如下语法 @@ -831,10 +831,10 @@ meeting_with_room_and_agenda_wo示例: - 上下文 public class meeting_room { - storey storey; // 楼层id + storey storey; // 楼层对象 String name; // 名称 room_type_enum room_type; // 会议室类型 - List equipment; // 设备 + List equipment; // 设备列表 Long id; // 主键 Long seat_number; // 座位数 String description; // 说明 @@ -842,10 +842,10 @@ meeting_with_room_and_agenda_wo示例: String input_code; // 输入码 Long storey_id; // 楼层id Long contact_staff_id; // 联系员工ID - List meeting; //注意:该字段可添加过滤条件 - class storey { // 建筑的楼层 - location location; // 位置id - String name; // 楼层 + List meeting; // 会议列表,可添加过滤条件 + class storey { // 楼层 + location location; // 位置对象 + String name; // 楼层名 Long id; // 主键 Long sort_number; // 排序号 Long location_id; // 位置id @@ -859,18 +859,18 @@ meeting_with_room_and_agenda_wo示例: String description; // 描述 Long create_user_id; // 创建人id } - class location { // 位置信息,如楼栋 + class location { // 位置,如楼栋 String name; // 名称 Long id; // 主键 String description; // 描述 Long sort_number; // 排序号 } - enum room_type_enum { // + enum room_type_enum { SMALL, //小会议室 MEDIUM, //中会议室 LARGE } - enum equipment_enum { // + enum equipment_enum { TV, //电视 WHITEBOARD, //白板 PROJECTOR, //投影仪 @@ -879,8 +879,8 @@ meeting_with_room_and_agenda_wo示例: } - 用户需求 - 获取一段时间未被使用的会议室(包含当前会议已选择的会议室),并且根据会议名称会议信息,最后分页返回。 - - 对应的读方案定义 + 获取一段时间未使用的会议室(包含当前会议已选择的会议室),根据会议标题过滤会议信息,分页返回。 + - 读方案定义 { "name": "get_unused_meeting_room_list", @@ -901,45 +901,45 @@ meeting_with_room_and_agenda_wo示例: } - **代码产物和修改建议** - - **查询传输对象(QTO)** - * **生成产物:** 在Service层生成一个Java类 + - **QTO查询参数对象** + * **生成产物:** Service层生成Java类 * **命名规则:** 类名以Qto结尾(${ReadPlanName}Qto) - * **类路径:** 位于**.persist.qto包路径下 - * **职责:** 读方案的查询参数结构,通常可作为API的参数,API接收到参数后可直接透传给内部的RPC进行调用 - * **唯一标识符位置:** 其对应的唯一标志在类注解@AutoGenerated中指定,uuid规则: ${ReadPlan在TOCO中的uuid}|QTO|DEFINITION - * **内部结构:** QTO内部包含了如下分页相关的字段: + * **类路径:** `**.persist.qto`包下 + * **职责:** 读方案的查询参数结构,可作为API参数,直接透传给RPC调用 + * **唯一标识符位置:** 类注解@AutoGenerated中指定,uuid规则: ${ReadPlan的uuid}|QTO|DEFINITION + * **内部结构:** QTO包含分页相关字段: - private String scrollId; //瀑布流分页时用作游标 - private Integer size; //页码分页时用作每页记录的数量 - private Integer from; //页码分页时用作开始的记录位置 + private String scrollId; //瀑布流游标 + private Integer size; //每页记录数 + private Integer from; //开始记录位置 - **Dao** - - **生成产物:** 在Dao层生成一个Java类 + - **生成产物:** Dao层生成Java类 - **命名规则:** 类名以Dao结尾(${ReadPlanName}Dao) - - **类路径:** 位于 **.persist.mapper包路径下 - - **职责:** 读方案对应的数据库查询方法 - - **唯一标识符位置:** 其对应的唯一标志在类注解@AutoGenerated中指定,uuid规则: ${ReadPlan在TOCO中的uuid}|QTO|DAO + - **类路径:** `**.persist.mapper`包下 + - **职责:** 读方案对应的数据库查询方法 + - **唯一标识符位置:** 类注解@AutoGenerated中指定,uuid规则: ${ReadPlan的uuid}|QTO|DAO - **QueryExecutor** - - **生成产物** 对于返回**VO**的查询方案,在controller层生成一个Java类,对于分页查询、不分页返回全量、瀑布流查询、计数都有后独立的函数实现 - - 分页查询, 会生成一个 **Paged函数, 返回VSQueryResult - - 不分页返回全量, 会生成一个query**函数,返回List - - 瀑布流,会生成一个,**Waterfall函数,返回VSQueryResult - - 查询数量, 会生成一个 **Count函数,返回Integer + - **生成产物** 返回**VO**的查询方案,在controller层生成Java类,包含独立函数: + - 分页查询: **Paged函数,返回VSQueryResult + - 不分页全量: query**函数,返回List + - 瀑布流: **Waterfall函数,返回VSQueryResult + - 查询数量: **Count函数,返回Integer - **命名规则:** 类名以QueryExecutor结尾(${VoName}QueryExecutor) - - **类路径:** 位于**.entrance.web.query.executor包路径下 - - **职责:** 提供针对VO的查询入口,把QtoService返回的id数据转化成目标**VO** + - **类路径:** `**.entrance.web.query.executor`包下 + - **职责:** 提供VO查询入口,将QtoService返回的id数据转化为目标**VO** - **QueryService** - - **生成产物** 对于返回**DTO**的查询方案,在service层生成一个Java类,对于分页查询、不分页返回全量、瀑布流查询、计数都有后独立的函数实现 - - 分页查询, 会生成一个 **Paged函数,, 返回VSQueryResult - - 不分页返回全量, 会生成一个query**函数, 返回List - - 瀑布流,会生成一个,**Waterfall函数, 返回VSQueryResult - - 查询数量, 会生成一个 **Count函数,返回Integer + - **生成产物** 返回**DTO**的查询方案,在service层生成Java类,包含独立函数: + - 分页查询: **Paged函数,返回VSQueryResult + - 不分页全量: query**函数,返回List + - 瀑布流: **Waterfall函数,返回VSQueryResult + - 查询数量: **Count函数,返回Integer - **命名规则:** 类名以QueryService结尾(${DtoName}QueryService) - - **类路径:** 位于**.service.index.entity包路径下 - - **职责:** 提供对DTO查询入口,把QtoService返回的id数据转化成目标**DTO**,或返回符合条件的记录数量 - - **VSQueryResult说明:** - - VSQueryResult为一个写死的类,全路径为com.vs.es.query.VSQueryResult,存在于jar中,可以直接使用,禁止再创建新的VSQueryResult类! - - VSQueryResult代码结构如下: + - **类路径:** `**.service.index.entity`包下 + - **职责:** 提供DTO查询入口,将QtoService返回的id数据转化为目标**DTO**,或返回符合条件的记录数量 + - **VSQueryResult说明:** + - VSQueryResult是固定类,全路径为com.vs.es.query.VSQueryResult,存在于jar中,直接使用,禁止创建新的VSQueryResult类! + - VSQueryResult代码结构: package com.vs.es.query; @@ -952,16 +952,16 @@ meeting_with_room_and_agenda_wo示例: private int count; private List result; - private int from; //页码分页的起始条数 - private int size; //每页的记录数量 + private int from; //页码分页起始条数 + private int size; //每页记录数量 private String scrollId; //瀑布流查询游标 - private boolean hasMore; //瀑布流查询时表示是否有下一页 + private boolean hasMore; //瀑布流查询是否有下一页 } - **例子:** - * 根据用户名称查询用户列表返回UserDTO,则生成UserNameQto、UserNameQtoService、UserNameQtoDao、UserNameQueryService; UserNameQueryService调用UserNameQtoService,UserNameQtoService调用UserNameQtoDao - - **修改建议:** - - 如果有对结果的数据二次处理,建议在QueryService和QueryExecutor中进行代码扩展,不建议修改QTO文件 + * 根据用户名称查询用户列表返回UserDTO,生成UserNameQto、UserNameQtoService、UserNameQtoDao、UserNameQueryService; UserNameQueryService调用UserNameQtoService,UserNameQtoService调用UserNameQtoDao + - **修改建议:** + - 如有结果数据二次处理,建议在QueryService和QueryExecutor中扩展代码,不建议修改QTO文件 #### **2.10 查询传输对象(QTO)** - **定义与用途:** 在TOCO中,QTO为读方案的查询参数结构,每个读方案会对应一个QTO,读方案调用方按照QTO的结构向读方案生成的RPC方法传入需要查询的实体字段值,完成对数据库的查询