前面的文章曾经提及过,我们可以使用 BeanUtils.copyProperties 进行对象的拷贝,可以用于 DO、DTO 等之间的转换,但是要注意它只能用于浅拷贝。所以如果你的对象是层层嵌套型,或者有数组 List 等等复杂的类型,其实 copyProperties 就很掣肘了。而且高并发下它会有性能问题,可能还没有 get set 效率高。具体原因后面再补充。我们回归正题。
下面介绍一种名为 Orika 的小框架,可以高效的实现深拷贝。
1. 准备
两个 DO 对象,即从数据库查询出的实体对象,Person 类,其中包含了几个常见类型的成员变量。
@Data@AllArgsConstructor@NoArgsConstructor@Builderpublic 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@NoArgsConstructorpublic class Student implements Serializable {private String schoolName;private Integer classId;}
下面是 Person 的 DTO 对象,其中的字段与 Person 存在对应,但是表达形式和名称可能不是很一致,例如 上面的 age 这里叫做 myAge,Integer 类型的 gender 性别,变成了一个枚举类,dateInfoStr 这个 json 字符串也变成了 dateInfo 对象。
@Data@AllArgsConstructor@NoArgsConstructor@Builderpublic 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@NoArgsConstructorpublic 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>() {@Overridepublic Integer convertTo(GenderEnum genderEnum, Type<Integer> type, MappingContext mappingContext) {return genderEnum.getCode();}@Overridepublic 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> {@Overridepublic String convertTo(T source, Type<String> destinationType, MappingContext mappingContext) {return JSON.toJSONString(source);}@Overridepublic 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---男
