Appearance
装配处理器
在一些场景中,一个 key 值会对应多个数据源对象,有时甚至会出现 key 本身就是一个数组或者集合的情况。此时,我们需要更换装配处理器 AssembleOperateHandler
并配合键值解析器 KeyResolver
来完成这种一对多/多对多装配。
装配处理器 AssembleOperateHandler
在整个装配过程中负责实际的属性读写操作,类似于 Jackson 中的序列化器(Serializer
)和反序列化器(Deserializer
)。与 Jackson 类似,如果我们需要处理特殊数据结构或具有特殊填充逻辑的 JavaBean,就需要更换不同的装配操作处理器。
crane4j 默认提供了三种处理器:
OneToOneAssembleOperationHandler
:一对一装配操作处理器,也是默认的处理器;OneToManyAssembleOperationHandler
:一对多装配操作处理器;ManyToManyAssembleOperationHandler
:多对多装配操作处理器;
1.一对一
你可以在 @Assemble
注解的 handler
属性或 handlerType
属性中指定要使用的处理器。比如:
java
public class Foo {
@Assemble(container = "foo", handlerType = OneToOneAssembleOperationHandler.class)
private String name;
private String alias;
}
当你不指定时,默认将会使用一对一装配处理器 OneToOneAssembleOperationHandler
执行操作,即一个 key
值只对应一个数据源对象。
2.一对多
在一对多的情况下,一个属性对应多个数据源对象,即从数据源容器中查询数据时,一个 key 可以查出一个集合或数组。比如:
我们有一个名为 customer
的容器,可以根据客户 id 查询客户,并返回按客户组别 id 分组的 Map
集合:
java
// 根据ID查询客户,返回的数据按客户组别ID分组
Container<String> customerContainer = LambdaContainer.forLambda(
"customer", ids -> customerService.listByIds(ids)
.stream()
.collect(Collectors.groupBy(CustomerDO::getGroupId))
);
2.1.使用一对多装配处理器
现在,我们可以借助一对多装配处理器,批量提取数据源对象的属性,并将其赋值给目标对象的指定属性:
java
public class CustomerVO {
@Assemble(
container = "customer",
handlerType = OneToManyAssembleOperationHandler.class,
props = @Mapping(src = "name", ref = "customerNames")
)
private Integer id;
private List<String> customerNames;
}
在上面的示例中,我们根据 id 集合获取按客户组别ID分组的客户集合,并提取 CustomerDO.name
作为集合,然后赋值给 CustomerVO.customerNames
。
2.2.使用参数对象作为 Key 值
在一对多的情况下,数据源容器(通常是接口中的查询方法)接受的参数有可能是一个参数对象,若在 2.7.0 及更高版本,你可以通过下述方式来指定如何生成参数对象:
java
@Assemble(
container = "customer",
handlerType = OneToManyAssembleOperationHandler.class,
props = @Mapping(src = "name", ref = "customerNames"),
keyType = CustomerQueryDTO.class, // 指定参数对象类型,该类必须有一个公开的无参构造方法
keyDesc = "id:prop1, type:prop2" // 指定如何将属性值映射到参数对象
)
@Data
public class CustomerVO {
private Integer id;
private String type;
private List<String> customerNames;
}
@Data
public class CustomerQueryDTO {
private String prop1;
private String prop2;
}
具体可参见 声明装配操作 中 “键的解析策略” 一节。
3.多对多
在多对多的情况下,多个键(key)对应多个数据源对象,实际场景中,比较常见的情况有三种:
- 键字段是按特定分隔符拼接的字符串;
- 建字段是集合类型;
- 建字段是数组类型;
例如,假设存在以下键字段类型:
java
private String idStr; // 键字段为按分隔符拼接的字符串,例如:"a, b, c"
private Set<Integer> idList; // 键字段为集合,例如:[a, b, c]
private Integer[] idArray; // 键字段为数组,例如:[a, b, c]
3.1.使用多对多装配处理器
针对多个键字段映射,需要使用特定的装配操作处理器 ManyToManyAssembleOperationHandler
:
java
public class StudentVO {
@Assemble(
container = "teacher",
handlerType = ManyToManyAssembleOperationHandler,
props = @Mapping(src = "name", ref = "teacherNames")
)
private String teacherIds; // 默认支持 "1, 2, 3" 格式
private List<String> teacherNames; // 填充的格式默认为 src1, src2, src3
}
在上面的示例中,根据 teacherIds
字段字符串中按分隔符分割的多个键值,查询关联的多个 Teacher
对象,然后将 Teacher
集合的 name
属性映射为 List<String>
并赋值给 StudentVO.teacherNames
字段。
这种字段映射遵循普通字段映射的语义,例如对象映射:
java
public class StudentVO {
@Assemble(
container = "teacher",
props = @Mapping(ref = "teachers"),
handlerType = ManyToManyAssembleOperationHandler
)
private String teacherIds; // 默认支持 "1, 2, 3" 格式
private List<Teacher> teachers;
}
在批量映射的情况下,返回的对象可以是数据源对象或数据源对象的属性集合。
3.2.更换分隔符
默认情况下,若返回值为字符串,ManyToManyAssembleOperationHandler
总是尝试将其根据 “,
” 符号分割为字符串集合,但如果有必要,你也可以通过 keyDesc
属性指定要使用的分隔符。
java
@Data
public class Foo {
@Assemble(
container = "foo",
keyDesc = "|", // 指定使用 “|” 作为分隔符
keyType = Integer.class, // 指定将分割出的每个 key 都转为 Integer 类型
props = @Mapping(ref = "teachers")
)
private String teacherIds;
private List<Teacher> teachers;
}
具体可参见 声明装配操作 中 “键的解析策略” 一节。
3.3.拼接字符串
考虑到不少用户反映在这种场景下会有拼接字符串后再填充的需求,因此在 2.9.0 及以上版本新增了一个策略,你可以用它实现类似的效果:
java
@Data
public class Foo {
@Assemble(
container = "foo", props = @Mapping(src = "name", ref = "teacherNames"),
keyDesc = ",", // 指定使用 “,” 作为分隔符
propertyMappingStrategy = PropertyMappingStrategy.Coll_JOIN_AS_STRING // 将得到的 name 根据分隔符拼接为字符串
)
private String teacherIds; // "1, 2, 3"
private String teacherNames; // "name1,name2,name3"
}
在上述配置中,当进行填充时,name 集合将会被拼接位字符串再进行填充,字符串的分隔符与上文 keyDesc
指定的分隔符一致(默认为 ,
)。