# mybatis-accessor
**Repository Path**: lumineth/mybatis-accessor
## Basic Information
- **Project Name**: mybatis-accessor
- **Description**: 基于mybatis-plus的扩展框架,通过DTO建立各实体间的关系,实现关联查询、级联更新、更新监听等功能。
- **Primary Language**: Java
- **License**: Apache-2.0
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 4
- **Forks**: 0
- **Created**: 2022-01-19
- **Last Updated**: 2025-02-25
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
### 1.特性
* 不编写SQL的情况下实现表的CRUD、关联、分组、排序等操作
* 通过SPEL表达式支持复杂的关联关系
* 支持级联新增、更新、删除
* 通过更新监听器监听表或字段的更新事件
* 预定义更新监听器,完成自动填充、统计、分配等功能
* 不影响Mybatis、Mybatis-Plus的原有功能
### 2.依赖
* mybatis-plus
### 3.使用前提
* 表必须设置主键,且主键自动生成(数据库自动生成或者使用mybatis-plus注解生成)
* 存在外键关系的表,外键的变量名与主键保持一致
* 遵循mybatis-plus的规则,定义service,mapper和entity
* 定义DTO类并使用DtoEntityRelation注解维护DTO、ENTITY、SERVICE的关系
### 4.引入依赖
```xml
com.circustar
mybatis-accessor
1.2.4
```
### 5.定义Mybatis-plus相关类
#### 5.1定义POJO实体类
```java
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder(toBuilder = true)
public class Student implements Serializable {
//POJO
}
```
#### 5.2定义MAPPER(注意是CommonMapper)
```java
public interface StudentMapper extends CommonMapper {
}
```
#### 5.3定义ISERVICE
```java
public interface IStudentService extends IService {
}
```
### 6.定义DTO并关联Entity和service, DtoEntityRelation注解可定义在DTO上或者ENTITY上
```java
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder(toBuilder = true)
@DtoEntityRelation(entityClass = Student.class, service = IStudentService.class)
public class StudentDto {
//POJO
}
```
### 7.开启包扫描,扫描CommonMapper类以及DtoEntityRelation注解
```java
@MapperScan("com.test.mybatis_accessor.mapper")
@EnableMybatisAccessor(
relationScan = @RelationScanPackages({"com.test.mybatis_accessor.entity", "com.test.mybatis_accessor.dto"})
)
public class SpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(WebTestApplication.class, args);
}
}
```
### 8.快速使用
#### 8.1.查询列表
* 定义DTO
```java
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder(toBuilder = true)
@DtoEntityRelation(entityClass = Student.class, service = IStudentService.class)
public class StudentDto {
private Long studentId;
@QueryWhere(connector = Connector.likeRight)
private String name;
@QueryOrder(sortIndex = 1, sortOrder = QueryOrder.ORDER_DESC)
private Integer grade;
}
```
* 使用
```java
StudentDto studentDto = new StudentDto();
studentDto.setName(studentName);
// 查询时会自动加上where条件 : name like ? || '%',并按grade降序排序
List list = mybatisAccessorService.getDtoListByAnnotation(studentQueryDto);
```
#### 8.2.分页查询
```java
StudentDto studentDto = new StudentDto();
studentDto.setName(studentName);
// 分页获取数据
List list = mybatisAccessorService.getDtoPageByAnnotation(studentQueryDto,1,10);
```
#### 8.3.分组获取数据
* 定义ENTITY
```java
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder(toBuilder = true)
@TableName("student")
public class StudentGenderGroup {
private Integer classId;
private Integer gender;
@TableField(exist=false)
private BigDecimal averageAge;
}
```
* 定义DTO
```java
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder(toBuilder = true)
@DtoEntityRelation(entityClass = StudentGenderGroup.class, service = IStudentGenderGroupService.class)
public class StudentGenderGroupDto {
@QueryGroupBy
private Integer classId;
@QueryGroupBy
private Integer gender;
@QueryColumn("round(avg(age),2)")
private BigDecimal averageAge; // 表中不存在的字段需要在entity中定义相同名称的字段,并标注@TableField(exist=false)
@QueryGroupBy
@QueryHaving(expression = "count(*) < 10")
private Integer gender;
}
```
* 使用
```java
StudentGenderGroupDto studentDto = new StudentGenderGroupDto();
// 查询SQL: select class_id, gender, round(avg(age),2) as average_age from student group by class_id, gender
List list = mybatisAccessorService.getDtoListByAnnotation(studentQueryDto);
```
#### 8.4.获取单条数据
```java
//根据ID获取单条数据并获取所有子项
StudentGenderGroupDto list = getDtoById.getDtoListById(StudentGenderGroupDto.class, 1L, true, null);
//根据queryDto设置的查询条件获取单条数据并获取特定子项(scoreList)
StudentDto result = mybatisAccessorService.getDtoByAnnotation(queryDto, false
, Arrays.asList("scoreList"));
```
#### 8.5.查询单条数据完成后,再执行一条SQL语句获取子项,实现关联查询
* 与QueryJoin不同点
1.QueryJoin通过左连接,内联查查询相关表,只执行一次;Selector在查询完成后,再执行一条SQL查询关联表,需执行多次。
2.QueryJoin需要在ENTITY中定义DTO中相同名称的变量,Selector不需要
3.Selector只能在获取单个对象后,再去查询关联表(对于mybatisAccessorService.getDtoListXXX,mybatisAccessorService.getEntityListXXX无效);QueryJoin无限制。
4.使用主键关联时,Selector注解可省略
* 定义
```java
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder(toBuilder = true)
@DtoEntityRelation(entityClass = Student.class, service = IStudentService.class)
public class StudentDto2 {
private Integer studentId;
@Selector(tableColumn = "student_id", connector = Connector.eq
, valueExpression = "#{studentId}")
private List scoreList;
}
```
* 使用
```java
StudentDto2 entity = (StudentDto2) mybatisAccessorService.getDtoById(StudentDto2.class, 1, true, null);
```
#### 8.6.使用SQL连接获取子项,实现关联查询(支持左连接LEFT JOIN、内连接INNER JOIN)
* 定义DTO
```java
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder(toBuilder = true)
@DtoEntityRelation(entityClass = Score.class, service = IScoreService.class)
public class ScoreDto implements Serializable {
private Integer scoreId;
private Integer studentId;
// 使用主键关联,可忽略joinExpression属性
// 仅关联一次的表,可忽略tableAlias属性
// join无顺序要求,可忽略Order属性
@QueryJoin(tableAlias = "student", joinExpression = "student.student_id = score.student_id and score.deleted = 0" , order = 1)
private StudentDto student;
}
```
* ENTITY中加入对应的字段
```java
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder(toBuilder = true)
public class Score implements Serializable {
private Integer scoreId;
private Integer studentId;
// 必须标注TableField(exist = false), 否则报错
@TableField(exist = false)
private Student student;
}
```
* 使用
```java
ScoreDto scoreDto = new ScoreDto();
scoreDto.setStudentId(1);
List list = mybatisAccessorService.getDtoListByAnnotation(scoreDto);
```
### 9.数据更新
* 级联新增
```java
StudentDto studentDto = new StudentDto();
studentDto.setName(name);
List scoreDtoList = new ArrayList<>();
ScoreDto score1 = ScoreDto.builder().courseId(1).name("c_" + name).score(BigDecimal.valueOf(120)).build();
scoreDtoList.add(score1);
studentDto.setScoreList(scoreDtoList);
List courseDtoList = new ArrayList<>();
StudentCourseDto course1 = StudentCourseDto.builder().courseId(1).selectDate(new Date()).build();
courseDtoList.add(course1);
studentDto.setCourseList(courseDtoList);
// 保存数据(将保存三个表:Student,Score,StudentCourse)
Student updateResult = mybatisAccessorService.save(studentDto, true, null, false,null);
```
* 级联更新(可同时保存/更新/删除)
```java
StudentDto studentDto = mybatisAccessorService.getDtoByAnnotation(queryDto, true, null);
// 更新Course
studentDto.getCourseList().get(0).setCourseName("数学课");
// 新增Course
studentDto.getCourseList().add(StudentCourseDto.builder().courseId(10).courseName("语文课").build());
// 删除Score(需要再Deleted字段上标注DeleteFlag注解)
studentDto.getScoreList().get(0).setDeleted(1);
// 更新Score
studentDto.getScoreList().get(1).setScore(100);
mybatisAccessorService.update(studentDto, true, null, false, false,null);
```
* 级联删除
```java
// 删除主表Student以及子表Score,Course
mybatisAccessorService.deleteByIds(StudentDto.class, idList, Arrays.asList("scoreList","courseList"), false,null);
```
### 11.更新监听器
* 更新前或更新后,完成数据查找、数字统计、分配、执行特定SQL或者其他Bean的方法
* 三种更新监听器,DecodeEvent完成更新前从其他表获取信息并赋值到待更新DTO
* UpdateEvent与PropertyChangeEvent功能相似,完成数字的统计、分配、执行SQL、执行特定Bean的方法
* PropertyChangeEvent监听字段的变化,在字段产生变化时才会执行。UpdateEvent不监听字段变化。
* PropertyChangeEvent查找原值并与更新值做比较,开销比UpdateEvent大。
#### 10.1.DecodeEvent
```java
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder(toBuilder = true)
@DtoEntityRelation(entityClass = ProductOrderDetail.class, service = IProductOrderDetailService.class)
//更新前根据ProductOrderDetail3Dto中productId的值,从Product表中获取productName,并设置到ProductOrderDetail3Dto中
@DecodeEvent(onExpression = "", targetProperties = "productName", matchProperties = "productId", sourceDtoClass = ProductDto.class)
public class ProductOrderDetail3Dto extends BaseDto implements Serializable {
private Long orderDetailId;
private Integer productId;
private String productName;
}
```
#### 10.2.UpdateEvent
```java
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder(toBuilder = true)
@DtoEntityRelation(entityClass = ProductOrder.class, service = IProductOrderService.class)
//ProductOrderDto2更新后用orderDetails的个数更新totalCount的值。
@UpdateEvent(onExpression = "#{orderDetails?.size() > 0}", updateEventClass = UpdateCountEvent.class, updateParams = {"totalCount", "orderDetails"})
public class ProductOrderDto2 extends BaseDto implements Serializable {
private Integer orderId;
private Integer totalCount;
private List orderDetails;
}
```
#### 10.3.PropertyChangeEvent
```java
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder(toBuilder = true)
@DtoEntityRelation(entityClass = ProductOrderDetail.class, service = IProductOrderDetailService.class)
//weight发生变化(仅新增或者更新,删除时不触发)时,调用executeUpdateBean的test方法,参数为orderDetailId
@PropertyChangeEvent(listenProperties = "weight", updateEventClass = UpdateExecuteBeanMethodEvent.class, updateParams = {"executeUpdateBean", "test", "orderDetailId"}, updateType = {IUpdateCommand.UpdateType.INSERT, IUpdateCommand.UpdateType.UPDATE})
public class ProductOrderDetail3Dto extends BaseDto implements Serializable {
private Integer orderDetailId;
private Integer orderId;
private BigDecimal weight;
}
```
### 11.更新管理MybatisAccessorUpdateManager
* 将需要更新的DTO,加入MybatisAccessorUpdateManager中,需要执行更新时,调用submit方法
* MybatisAccessorUpdateManager会将待更新DTO按照对应Entity中UpdateOrder注解定义的顺序以及Entity的名称排序后依次更新,避免因顺序引起的死锁问题
```java
public class Demo1 {
@Autowired
private MybatisAccessorUpdateManager updateManager;
public void addDto() {
updateManager.putDto(dto01);
updateManager.putDto(dto02);
updateManager.putDto(dtoList01);
updateManager.submit();
}
}
```
### 12.注解详细说明
#### 12.1.设置相关
##### 12.1.1.DtoEntityRelation
* 说明:定义DTO、Entity与Service的关系,作用于Entity类或者Dto类上
* 参数1 - entityClass : 定义与DTO有关系的Entity类,定义在Entity类上时可省略
* 参数2 - dtoClass : 定义与Entity有关系的Dto类,定义在Dto类上时可省略
* 参数3 - service : 定义CRUD的操作类,需要继承MybatisPlus的IService接口
* 范例:
```java
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder(toBuilder = true)
@DtoEntityRelation(entityClass = Student.class, service = IStudentService.class)
public class StudentDto {
}
```
##### 12.2.2.EnableMybatisAccessor
* 说明:开启MybatisAccessor相关功能,扫描DtoEntityRelation注解
* 参数 - relationScan : 定义DtoEntityRelation注解所在的包
* 范例:
```java
@MapperScan("com.test.mybatis_accessor.mapper")
@EnableMybatisAccessor(
relationScan = @RelationScanPackages({"com.test.mybatis_accessor.entity", "com.test.mybatis_accessor.dto"})
)
public class SpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(WebTestApplication.class, args);
}
}
```
#### 12.2.查询相关
##### 12.2.1.QueryColumn
* 说明:作用于Dto的字段上,定义SQL语句的查询项目
| 参数 | 名称 | 类型 | 必须 | 备注 |
| ---- | ---- | ---- | ---- | ---- |
| value | 自定义查询结果 | String | 否 | 省略时为Dto字段对应的表字段 |
* 范例:
```java
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder(toBuilder = true)
@DtoEntityRelation(entityClass = StudentStatistics.class, service = IStudentAvgScoreService.class)
public class StudentAverageScoreDto {
@QueryColumn("name || 'appendString'")
private String name;
}
```
##### 12.2.2.QueryWhere
* 说明:作用于Dto的字段上,定义SQL语句的筛选条件,筛选条件为tableColumn(字段) + connector(对比条件) + expression(值)
| 参数 | 名称 | 类型 | 必须 | 备注 |
| ---- | ---- | ---- | ---- | ---- |
| tableColumn | 筛选条件字段 | String | 否 | 省略时为Dto字段对应的表字段 |
| connector | SQL语句的对比条件 | enum | 否 | 默认等于(Connector.EQ) |
| expression | 筛选值表达式 | String | 是 | 支持SPEL表达式 |
* 范例:
```java
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder(toBuilder = true)
@DtoEntityRelation(entityClass = Student.class, service = IStudentService.class)
public class StudentQueryDto {
private Integer studentId;
//取出表中grade>=gradeMinValue的记录
@QueryWhere(tableColumn = "grade", connector = Connector.GE)
private Integer gradeMinValue;
}
```
##### 12.2.3.QueryGroup
* 说明:作用于Dto的字段上,定义SQL语句的分组
| 参数 | 名称 | 类型 | 必须 | 备注 |
| ---- | ---- | ---- | ---- | ---- |
| value | 分组字段 | String | 否 | 对应group by的字段。默认Dto字段对应的表字段名 |
* 范例:
```java
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder(toBuilder = true)
@DtoEntityRelation(entityClass = StudentGenderGroup.class, service = IStudentGenderGroupService.class)
public class StudentGenderGroupDto {
@QueryGroupBy
private Integer classId;
@QueryGroupBy
private Integer gender;
@QueryColumn("round(avg(age),2)")
private BigDecimal averageAge; // 表中不存在的字段需要在entity中定义相同名称的字段,并标注@TableField(exist=false)
}
```
##### 12.2.4.QueryHaving
* 说明:作用于Dto的字段上,定义SQL语句的分组过滤条件
| 参数 | 名称 | 类型 | 必须 | 备注 |
| ---- | ---- | ---- | ---- | ---- |
| value | 分组过滤条件 | String | 是 | 对应having的过滤条件 |
* 范例:
```java
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder(toBuilder = true)
@DtoEntityRelation(entityClass = StudentStatistics.class, service = IStudentAvgScoreService.class)
public class StudentGenderGroupDto {
@QueryGroupBy
private Integer classId;
@QueryGroupBy
@QueryHaving(expression = "count(*) < 10")
private Integer gender;
}
```
##### 12.2.5.QueryJoin
* 说明:作用于Dto的字段上,定义SQL语句的连接关系
| 参数 | 名称 | 类型 | 必须 | 备注 |
| ---- | ---- | ---- | ---- | ---- |
| tableAlias | join表的别名 | String | 否 | 默认表名 |
| joinType | 左连接还是内连接 | enum | 否 | 默认左连接 |
| joinExpression | join条件 | String | 是 | 支持SPEL表达式 |
| order | join顺序 | int | 否 | 默认1 |
* 范例:
```java
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder(toBuilder = true)
@DtoEntityRelation(entityClass = Score.class, service = IScoreService.class)
public class ScoreDto implements Serializable {
private Integer scoreId;
private Integer studentId;
// 使用主键关联,可忽略joinExpression属性
// 仅关联一次的表,可忽略tableAlias属性
// join无顺序要求,可忽略Order属性
@QueryJoin(tableAlias = "student", joinExpression = "student.student_id = score.student_id and score.deleted = 0" , order = 1)
private StudentDto student;
}
```
##### 12.2.6.Selector
* 说明:作用于Dto的字段上。在获取单个DTO对象后,再根据Selector定义的条件查询相关记录
| 参数 | 名称 | 类型 | 必须 | 备注 |
| ---- | ---- | ---- | ---- | ---- |
| tableColumn | 筛选条件字段 | String | 否 | 省略时为Dto字段对应的表字段 |
| connector | SQL语句的对比条件 | enum | 否 | 默认等于(Connector.EQ) |
| valueExpression | 筛选值表达式 | String | 是 | 支持SPEL表达式 |
| order | Selector执行的顺序 | int | 否 | |
* 范例:
```java
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder(toBuilder = true)
@DtoEntityRelation(entityClass = Student.class, service = IStudentService.class)
public class StudentDto2 {
private Integer studentId;
@Selector(tableColumn = "student_id", connector = Connector.eq
, valueExpression = "#{studentId}")
private List scoreList;
}
```
##### 12.2.7.QueryOrder注解
* 说明:作用于Dto的字段上。定义排序。
| 参数 | 名称 | 类型 | 必须 | 备注 |
| ---- | ---- | ---- | ---- | ---- |
| expression | 排序表达式 | boolean | 否 | 支持SPEL,可省略。默认按作用字段排序 |
| sortIndex | 排序顺位 | int | 否 | 默认Integer.MAX_VALUE |
| sortOrder | 升序还是降序 | enum | 否 | 默认升序 |
* 范例:
```java
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder(toBuilder = true)
@DtoEntityRelation(entityClass = Student.class, service = IStudentService.class)
public class StudentDto {
@QueryOrder(sortIndex = 1, sortOrder = QueryOrder.ORDER_DESC)
private String name;
}
```
#### 12.3.更新相关
##### 12.3.1.IdReference
* 说明:作用于Entity的字段上,表示该字段的值对应的是本表的一个ID。
* 参数:无
* 范例:
```java
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder(toBuilder = true)
public class PersonInfo extends BaseEntity implements Serializable {
@TableId(type = IdType.AUTO)
private Integer personId;
//代表上级的personId
@IdReference
private Integer leaderId;
private String personName;
}
```
##### 12.3.2.UpdateCascade
* 说明:作用于Dto的字段上。定义是否支持字段的级联更新
| 参数 | 名称 | 类型 | 必须 | 备注 |
| ---- | ---- | ---- | ---- | ---- |
| value | 是否级联更新 | boolean | 否 | 默认true |
* 范例:
```java
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder(toBuilder = true)
@DtoEntityRelation(entityClass = Score.class, service = IScoreService.class)
public class ScoreDto implements Serializable {
private Integer scoreId;
private Integer studentId;
//更新ScoreDto时不会更新StudentDto
@UpdateCascade(false)
@QueryJoin(tableAlias = "student", joinExpression = "student.student_id = score.student_id and score.deleted = 0" , order = 1)
private StudentDto student;
}
```
##### 12.3.3.DeleteFlag
* 说明:作用于Dto的字段上。如果字段值是1,更新或者级联更新时会删除ID对应的记录
| 参数 | 名称 | 类型 | 必须 | 备注 |
| ---- | ---- | ---- | ---- | ---- |
| physicDelete | 是否物理删除 | boolean | 否 | 默认false |
* 范例:
```java
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder(toBuilder = true)
@DtoEntityRelation(entityClass = StudentCourse.class, service = IStudentCourseService.class)
public class StudentCourseDto {
private Integer studentCourseId;
@DeleteFlag
private Integer deleted;
private Integer version;
}
```
##### 12.3.4.DeleteAndInsertNewOnUpdate
* 说明:作用于Dto的字段上。更新时,使用删除再插入的方式代替更新
| 参数 | 名称 | 类型 | 必须 | 备注 |
| ---- | ---- | ---- | ---- | ---- |
| value | 是否开启 | boolean | 否 | 默认true |
| deleteEvenIfEmpty | 即使空也删除 | boolean | 否 | true时表示即使Dto字段即使是空也会进行删除,默认false |
* 范例:
```java
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder(toBuilder = true)
@DtoEntityRelation(entityClass = Student.class, service = IStudentService.class)
public class StudentDto6 implements Serializable {
private Integer studentId;
@DeleteAndInsertNewOnUpdate(value = true, deleteEvenIfEmpty = true)
private List courseList;
}
```
##### 12.3.5.UpdateOrder
* 说明:作用于Entity的字段上,定义更新优先级,与MybatisAccessorUpdateManager一起使用
| 参数 | 名称 | 类型 | 必须 | 备注 |
| ---- | ---- | ---- | ---- | ---- |
| value | 优先级 | int | 是 | 越小优先级越高 |
#### 12.4.监听器相关
##### 12.4.1.DecodeEvent
* 说明:作用于Dto类上。在更新前获取其他表信息并设置到DTO中
| 参数 | 名称 | 类型 | 必须 | 备注 |
| ---- | ---- | ---- | ---- | ---- |
| onExpression | 开启监听器条件 | String | 否 | 支持SPEL |
| sourceDtoClass | 数据来源DTO类 | Class | 是 | |
| sourceProperties | 数据来源字段 | String[] | 否 | 与targetProperties一致时可省略 |
| targetProperties | 目标字段 | String[] | 是 | |
| matchSourceProperties | 数据来源过滤条件字段名 | String[] | 否 | 与matchProperties一致时可省略 |
| matchProperties | 目标Dto类对应过滤条件字段名 | String[] | 是 | |
| errorWhenNotExist | 找不到数据源时报错 | boolean | 否 | 默认true |
| UpdateType | 更新类型 | Enum | 否 | 更新类型,默认新增/修改 |
| executeTiming | 更新时机 | Enum | 否 | 监听器执行时机,默认更新前 |
* 范例:
```java
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder(toBuilder = true)
@DtoEntityRelation(entityClass = OrderDetail.class, service = IOrderDetailService.class)
//更新时根据productId去Product表中获取productName并设置到OrderDetailDto.productName中
@DecodeEvent(onExpression = "", targetProperties = "productName", matchProperties = "productId"
, sourceDtoClass = ProductDto.class)
public class OrderDetailDto extends BaseDto implements Serializable {
private Integer orderDetailId;
private Integer productId;
private String productName;
}
```
##### 12.4.2.UpdateEvent
* 说明:作用于Dto类上。监听器在更新前后执行指定的操作。
| 参数 | 名称 | 类型 | 必须 | 备注 |
| ---- | ---- | ---- | ---- | ---- |
| onExpression | 开启监听器条件 | String | 否 | 支持SPEL |
| updateEventClass | 执行监听类 | String | 是 | 必须实现IUpdateEvent接口 |
| updateParams | 参数 | String | 否 | 只能是String类型 |
| UpdateType | 更新类型 | Enum | 否 | 默认updateEventClass中实现getDefaultUpdateTypes方法的值 |
| executeTiming | 更新时机 | Enum | 否 | 默认updateEventClass中实现getDefaultExecuteTiming方法的值 |
* 范例:
```java
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder(toBuilder = true)
@DtoEntityRelation(entityClass = PersonInfo.class, service = IPersonInfoService.class)
//更新完成后,将personInfoList的条数更新到teamCount对应的字段中去
@UpdateEvent(onExpression = "", updateEventClass = UpdateCountEvent.class, updateParams = {"teamCount", "personInfoList"})
public class PersonInfoDto extends BaseDto implements Serializable {
private Integer personId;
private Integer leaderId;
private String personName;
private Integer teamCount;
private Integer deleted;
private List personInfoList;
}
```
###### 预定义的IUpdateEvent实现类
* 1.UpdateCountEvent : 更新完成后将子项列表的条数更新到主项的字段中去。
| 参数 | 名称 | 类型 | 必须 | 备注 |
| ---- | ---- | ---- | ---- | ---- |
| 参数1 | 主项字段名 | String | 是 | |
| 参数2 | 子项列表名 | String | 是 | |
* 2.UpdateSumEvent : 更新完成后将子项列表指定字段的合计数更新到主项的字段中去。
| 参数 | 名称 | 类型 | 必须 | 备注 |
| ---- | ---- | ---- | ---- | ---- |
| 参数1 | 主项字段名 | String | 是 | |
| 参数2 | 子项列表名 | String | 是 | |
| 参数3 | 子项字段名称 | String | 是 | |
* 3.UpdateMaxEvent : 更新完成后将子项列表指定字段的最大值更新到主项的字段中去。
| 参数 | 名称 | 类型 | 必须 | 备注 |
| ---- | ---- | ---- | ---- | ---- |
| 参数1 | 主项字段名 | String | 是 | |
| 参数2 | 子项列表名 | String | 是 | |
| 参数3 | 子项字段名称 | String | 是 | |
* 4.UpdateMinEvent : 更新完成后将子项列表指定字段的最小值更新到主项的字段中去。
| 参数 | 名称 | 类型 | 必须 | 备注 |
| ---- | ---- | ---- | ---- | ---- |
| 参数1 | 主项字段名 | String | 是 | |
| 参数2 | 子项列表名 | String | 是 | |
| 参数3 | 子项字段名称 | String | 是 | |
* 5.UpdateAvgEvent : 更新完成后将子项列表指定字段的平均值更新到主项的字段中去。
| 参数 | 名称 | 类型 | 必须 | 备注 |
| ---- | ---- | ---- | ---- | ---- |
| 参数1 | 主项字段名 | String | 是 | |
| 参数2 | 子项列表名 | String | 是 | |
| 参数3 | 子项字段名称 | String | 是 | |
* 6.UpdateAssignEvent : 更新完成后将主项字段值根据子项中的权重比例分摊到子项字段上。
| 参数 | 名称 | 类型 | 必须 | 备注 |
| ---- | ---- | ---- | ---- | ---- |
| 参数1 | 主项字段名 | String | 是 | |
| 参数2 | 子项列表名 | String | 是 | |
| 参数3 | 子项字段名称 | String | 是 | |
| 参数4 | 精度 | String | 是 | |
| 参数5 | 子项权重字段名 | String | 是 | |
* 7.UpdateAvgAssignEvent : 更新完成后将主项字段值平均地分摊到子项字段上。
| 参数 | 名称 | 类型 | 必须 | 备注 |
| ---- | ---- | ---- | ---- | ---- |
| 参数1 | 主项字段名 | String | 是 | |
| 参数2 | 子项列表名 | String | 是 | |
| 参数3 | 子项字段名称 | String | 是 | |
| 参数4 | 精度 | String | 是 | |
* 8.UpdateFillEvent : 更新完成后将主项字段值按一定顺序分配到子项字段上,子项字段达到最大值后再分配下一条记录
| 参数 | 名称 | 类型 | 必须 | 备注 |
| ---- | ---- | ---- | ---- | ---- |
| 参数1 | 主项字段名 | String | 是 | |
| 参数2 | 剩余值设置字段 | String | 是 | 将分配剩余的值保存到该字段中 |
| 参数3 | 子项列表名 | String | 是 | |
| 参数4 | 子项字段名称 | String | 是 | |
| 参数5 | 填充最大值 | String | 是 | 可使用数值或者子项字段名 |
| 参数6 | 排序字段 | String | 否 | |
| 参数7 | 升序降序 | String | 否 | ASC或DESC |
* 9.UpdateExecuteSqlEvent : 更新完成后执行指定SQL
| 参数 | 名称 | 类型 | 必须 | 备注 |
| ---- | ---- | ---- | ---- | ---- |
| 参数1 | SQL语句 | String | 是 | 支持SPEL表达式 |
* 10.UpdateLogEvent : 更新前打印日志
| 参数 | 名称 | 类型 | 必须 | 备注 |
| ---- | ---- | ---- | ---- | ---- |
| 参数1 | 打印数据格式 | String | 否 | 支持SPEL表达式 |
* 11.UpdateExecuteBeanMethodEvent : 更新后执行指定Bean的方法
参数1:bean名称。参数2:执行方法。参数3->参数n:Dto字段名称,对应的值会作为执行方法的参数。不存在参数3时会将整个Dto作为参数。
| 参数 | 名称 | 类型 | 必须 | 备注 |
| ---- | ---- | ---- | ---- | ---- |
| 参数1 | bean名称 | String | 是 | |
| 参数2 | 执行方法 | String | 是 | 将分配剩余的值保存到该字段中 |
| 参数3-N | 执行方法参数 | String | 否 | Dto字段名称对应的值作为参数,不存在参数3时会将整个Dto作为参数 |
* 12.UpdateAnyEvent : 更新完成后将子项列表指定字段的任意值更新到主项的字段中去。
| 参数 | 名称 | 类型 | 必须 | 备注 |
| ---- | ---- | ---- | ---- | ---- |
| 参数1 | 主项字段名 | String | 是 | |
| 参数2 | 子项列表名 | String | 是 | |
| 参数3 | 子项字段名称 | String | 是 | |
##### 12.4.3.PropertyChangeEvent
* 说明:作用于Dto类上。监听到属性变化时,执行对应的IUpdateEvent实现类
| 参数 | 名称 | 类型 | 必须 | 备注 |
| ---- | ---- | ---- | ---- | ---- |
| listenProperties | 监听字段列表 | String[] | 否 | |
| fromExpression | 更新前的过滤条件 | String | 否 | SPEL表达式 |
| toExpression | 更新后的过滤条件 | String | 否 | SPEL表达式 |
| updateEventClass | 更新后执行的操作类 | Class | 是 | 必须实现IUpdateEvent接口 |
| updateParams | dto类成员变量名列表 | String[] | 否 | |
| UpdateType | 更新类型 | Enum | 否 | 默认updateEventClass中实现getDefaultUpdateTypes方法的值 |
| executeTiming | 更新时机 | Enum | 否 | 默认updateEventClass中实现getDefaultExecuteTiming方法的值 |
* 范例:
```java
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder(toBuilder = true)
@DtoEntityRelation(entityClass = ProductOrderDetail.class, service = IProductOrderDetailService.class)
//weight发生变化(新增或者更新时触发,删除不触发)时,调用executeUpdateBean的test方法,参数为orderDetailId的值
@PropertyChangeEvent(listenProperties = "weight", updateEventClass = UpdateExecuteBeanMethodEvent.class, updateParams = {"executeUpdateBean", "test", "orderDetailId"}, updateType = {IUpdateCommand.UpdateType.INSERT, IUpdateCommand.UpdateType.UPDATE})
public class ProductOrderDetail3Dto extends BaseDto implements Serializable {
private Integer orderDetailId;
private Integer orderId;
private BigDecimal weight;
}
```
### 13.主要类说明
#### 13.1.MybatisAccessorService
##### 13.1.1.getEntityById方法
* 说明:通过ID获取Entity
| 参数 | 名称 | 类型 | 非空 | 备注 |
| ---- | ---- | ---- | ---- | ---- |
| dtoClass | Dto类 | Class | 是 | |
| id | 主键值 | Seriable | 是 | |
##### 13.1.2.getEntityByAnnotation方法
* 说明:通过Dto类注解上的查询条件获取Entity
| 参数 | 名称 | 类型 | 非空 | 备注 |
| ---- | ---- | ---- | ---- | ---- |
| dto | Dto实例(查询条件) | Object | 是 | |
##### 13.1.3.getDtoById方法
* 说明:通过ID获取Dto
| 参数 | 名称 | 类型 | 非空 | 备注 |
| ---- | ---- | ---- | ---- | ---- |
| dtoClass | Dto类 | Class | 是 | |
| id | 主键值 | Seriable | 是 | |
| includeAllChildren | 是否查询所包含的所有子项 | boolean | 是 | |
| children | 指定需要查询的子项 | List | 否 | 仅includeAllChildren=false时生效 |
##### 13.1.4.getDtoByAnnotation方法
* 说明:通过Dto类注解上的查询条件获取Dto
| 参数 | 名称 | 类型 | 非空 | 备注 |
| ---- | ---- | ---- | ---- | ---- |
| dto | Dto实例(查询条件) | Object | 是 | |
| includeAllChildren | 是否查询所包含的所有子项 | boolean | 是 | |
| children | 指定需要查询的子项 | List | 否 | 仅includeAllChildren=false时生效 |
##### 13.1.5.getEntityListByAnnotation方法
* 说明:通过Dto类注解上的查询条件获取Entity的列表
| 参数 | 名称 | 类型 | 非空 | 备注 |
| ---- | ---- | ---- | ---- | ---- |
| dto | Dto实例(查询条件) | Object | 是 | |
##### 13.1.6.getDtoListByAnnotation方法
* 说明:通过Dto类注解上的查询条件获取Dto的列表
| 参数 | 名称 | 类型 | 非空 | 备注 |
| ---- | ---- | ---- | ---- | ---- |
| dto | Dto实例(查询条件) | Object | 是 | |
##### 13.1.7.getEntityPageByAnnotation方法
* 说明:通过Dto类注解上的查询条件获取Entity的列表
| 参数 | 名称 | 类型 | 非空 | 备注 |
| ---- | ---- | ---- | ---- | ---- |
| dto | Dto实例(查询条件) | Object | 是 | |
| pageIndex | 页码 | int | 是 | |
| pageSize | 每页记录数 | int | 是 | |
##### 13.1.8.getDtoPageByAnnotation方法
* 说明:通过Dto类注解上的查询条件获取Dto的列表
| 参数 | 名称 | 类型 | 非空 | 备注 |
| ---- | ---- | ---- | ---- | ---- |
| dto | Dto实例(查询条件) | Object | 是 | |
| pageIndex | 页码 | int | 是 | |
| pageSize | 每页记录数 | int | 是 | |
##### 13.1.9.save方法
* 说明:新增单个Dto,支持子项级联新增
| 参数 | 名称 | 类型 | 非空 | 备注 |
| ---- | ---- | ---- | ---- | ---- |
| insertObject | 新增Dto实例 | Object | 是 | |
| includeAllChildren | 是否新增所包含的所有子项 | boolean | 是 | |
| children | 指定需要新增的子项 | List | 否 | 仅includeAllChildren=false时生效 |
| updateChildrenOnly | 是否仅新增子项 | boolean | 是 | |
| updateEventLogId | 更新LOG ID | String | 否 | UpdateLogEvent会将updateEventLogId打印在日志信息中,null时会自动生成新的updateEventLogId。 |
##### 13.1.10.saveList方法
* 说明:新增Dto列表,支持子项级联新增
| 参数 | 名称 | 类型 | 非空 | 备注 |
| ---- | ---- | ---- | ---- | ---- |
| insertObjects | 新增Dto实例列表 | List | 是 | |
| includeAllChildren | 是否新增所包含的所有子项 | boolean | 是 | |
| children | 指定需要新增的子项 | List | 否 | 仅includeAllChildren=false时生效 |
| updateChildrenOnly | 是否仅新增子项 | boolean | 是 | |
| updateEventLogId | 更新LOG ID | String | 否 | UpdateLogEvent会将updateEventLogId打印在日志信息中,null时会自动生成新的updateEventLogId。 |
##### 13.1.11.update方法
* 说明:更新单个Dto,支持子项级联新增/修改/删除。子项ID为空时新增。子项ID不为空、无DeleteFlag注解或DeleteFlag注解对应属性的值不为1,修改。子项ID不为空、有DeleteFlag注解且DeleteFlag注解对应属性的值为1,删除。
| 参数 | 名称 | 类型 | 非空 | 备注 |
| ---- | ---- | ---- | ---- | ---- |
| updateObject | 更新Dto实例 | Object | 是 | |
| includeAllChildren | 是否更新所包含的所有子项 | boolean | 是 | |
| children | 指定需要更新的子项 | List | 否 | 仅includeAllChildren=false时生效 |
| updateChildrenOnly | 是否仅更新子项 | boolean | 是 | |
| updateEventLogId | 更新LOG ID | String | 否 | UpdateLogEvent会将updateEventLogId打印在日志信息中,null时会自动生成新的updateEventLogId。 |
##### 13.1.12.updateList方法
* 说明:更新Dto列表,支持子项级联新增/修改/删除。子项ID为空时新增。子项ID不为空、无DeleteFlag注解或DeleteFlag注解对应属性的值不为1,修改。子项ID不为空、有DeleteFlag注解且DeleteFlag注解对应属性的值为1,删除。
| 参数 | 名称 | 类型 | 非空 | 备注 |
| ---- | ---- | ---- | ---- | ---- |
| updateObjects | 更新Dto实例列表 | List | 是 | |
| includeAllChildren | 是否更新所包含的所有子项 | boolean | 是 | |
| children | 指定需要更新的子项 | List | 否 | 仅includeAllChildren=false时生效 |
| updateChildrenOnly | 是否仅更新子项 | boolean | 是 | |
| updateEventLogId | 更新LOG ID | String | 否 | UpdateLogEvent会将updateEventLogId打印在日志信息中,null时会自动生成新的updateEventLogId。 |
##### 13.1.13.DeleteByIds方法
* 说明:更新Dto列表,支持子项级联删除。
| 参数 | 名称 | 类型 | 非空 | 备注 |
| ---- | ---- | ---- | ---- | ---- |
| dtoClass | Dto类 | Class | 是 | |
| ids | id集合 | Set | 是 | |
| includeAllChildren | 是否删除所包含的所有子项 | boolean | 是 | |
| children | 指定需要删除的子项 | List | 否 | 仅includeAllChildren=false时生效 |
| updateChildrenOnly | 是否仅删除子项 | boolean | 是 | |
| updateEventLogId | 更新LOG ID | String | 否 | UpdateLogEvent会将updateEventLogId打印在日志信息中,null时会自动生成新的updateEventLogId。 |
#### 13.2.MybatisAccessorUpdateManager
##### 13.2.1.putDto方法
* 说明:将需要更新的对象,加入到MybatisAccessorUpdateManager的更新列表中。
| 参数 | 名称 | 类型 | 非空 | 备注 |
| ---- | ---- | ---- | ---- | ---- |
| dto | Dto实例或者Dto实例列表 | Object | 是 | |
##### 13.2.2.submit方法
* 说明:对MybatisAccessorUpdateManager的更新列表进行排序后依次更新