前面的文章曾经提及过,我们可以使用 BeanUtils.copyProperties 进行对象的拷贝,可以用于 DO、DTO 等之间的转换,但是要注意它只能用于浅拷贝。所以如果你的对象是层层嵌套型,或者有数组 List 等等复杂的类型,其实 copyProperties 就很掣肘了。而且高并发下它会有性能问题,可能还没有 get set 效率高。具体原因后面再补充。我们回归正题。
下面介绍一种名为 Orika 的小框架,可以高效的实现深拷贝。
1. 准备
两个 DO 对象,即从数据库查询出的实体对象,Person 类,其中包含了几个常见类型的成员变量。
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Person implements Serializable {
private String name;
private Integer age;
private Student student;
private List<String> phoneList;
private Integer gender;
/**
* Json 字符串
*/
private String dateInfoStr;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student implements Serializable {
private String schoolName;
private Integer classId;
}
下面是 Person 的 DTO 对象,其中的字段与 Person 存在对应,但是表达形式和名称可能不是很一致,例如 上面的 age 这里叫做 myAge,Integer 类型的 gender 性别,变成了一个枚举类,dateInfoStr 这个 json 字符串也变成了 dateInfo 对象。
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class PersonDTO implements Serializable {
private String name;
private Integer myAge;
private Student student;
private List<String> phoneList;
private GenderEnum gender;
private DateInfo dateInfo;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class StudentDTO implements Serializable {
private String schoolName;
private Integer classId;
}
public enum GenderEnum {
MALE(0, "男"),
FEMALE(1, "女");
private Integer code;
private String desc;
GenderEnum(Integer code, String desc) {
this.code = code;
this.desc = desc;
}
public Integer getCode() {
return code;
}
public String getDesc() {
return desc;
}
public static GenderEnum getValue(Integer code){
GenderEnum result = null;
GenderEnum[] values = GenderEnum.values();
for(GenderEnum genderEnum : values){
if(genderEnum.code.equals(code)){
result = genderEnum;
}
}
return result;
}
}
我们要做的就是把 Person 转为 PersonDTO 对象,即实现一次深拷贝。
2. 实现
直接贴出例子
- personList 就是你从数据库查询出的数据集合,类型是 Person,在静态代码块中准备数据。
- 在主函数中开始遍历处理集合
如果有一些特殊的匹配,需要自己处理下,例如下面注释中内容,如果都是比较基本的对象,而且名字都一一对应,其实不需要这么复杂。
public class CopyTest {
/**
* 拷贝前的集合
*/
private static List<Person> personList;
static {
// 数据准备
Person.PersonBuilder builder = Person.builder()
.name("张三")
.age(18)
.student(new Student("理想大学", 123))
.phoneList(Collections.singletonList("13666666666"))
.gender(0)
.dateInfoStr("{\"uid\": \"1023079620966365582\", \n \"username\": \"ideal-20\" \n }");
personList = new ArrayList<>();
personList.add(builder.build());
}
public static void main(String[] args) {
personList.forEach(personItem -> {
MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
// 处理 gender 这个字段 Integer 和 枚举类型之间的转换
mapperFactory.
getConverterFactory()
.registerConverter("genderEnumConvert", new BidirectionalConverter<GenderEnum, Integer>() {
@Override
public Integer convertTo(GenderEnum genderEnum, Type<Integer> type, MappingContext mappingContext) {
return genderEnum.getCode();
}
@Override
public GenderEnum convertFrom(Integer integer, Type<GenderEnum> type, MappingContext mappingContext) {
return GenderEnum.getValue(integer);
}
});
// 处理 dateInfo 这个字段 json字符串 和 对象之间的转换
mapperFactory
.getConverterFactory()
.registerConverter("dataInfoConvert", new JsonConfigConvert<DateInfo>());
// 其余配置
mapperFactory
.classMap(Person.class, PersonDTO.class)
.field("age", "myAge")
.fieldMap("gender", "gender").converter("genderEnumConvert").add()
.fieldMap("dateInfoStr", "dateInfo").converter("dataInfoConvert").add()
.byDefault()
.mapNulls(false)
.register();
// 转换
MapperFacade mapperFacade = mapperFactory.getMapperFacade();
PersonDTO personDTO = mapperFacade.map(personItem, PersonDTO.class);
System.out.println("DO -> DTO:" + personDTO);
System.out.println(personDTO.getGender().getCode() + "---" + personDTO.getGender().getDesc());
});
}
public static class JsonConfigConvert<T> extends BidirectionalConverter<T, String> {
@Override
public String convertTo(T source, Type<String> destinationType, MappingContext mappingContext) {
return JSON.toJSONString(source);
}
@Override
public T convertFrom(String source, Type<T> destinationType, MappingContext mappingContext) {
return JSON.parseObject(source, destinationType.getRawType());
}
}
}
运行结果:
DO -> DTO:PersonDTO(name=张三, myAge=18, student=Student(schoolName=理想大学, classId=123), phoneList=[13666666666], gender=MALE, dateInfo=DateInfo(uid=1023079620966365582, username=ideal-20))
0---男