开发中,操作属性时,经常遇到下面这种情况:
public static void main(String[] args) { | |
//tony 是新转学的学生,mike 是老生 | |
Student mike = new Student("Mike",12,"四年二班","Kite","1.4","40","合肥市蜀山区"); | |
Student tony = new Student("Tony",14,null,null,"1.5","50","合肥市包河区"); | |
// 将 tony 转到了 mike 所在的班级 | |
tony.setClassName(mike.getClassName()); | |
tony.setTeacherName(mike.getTeacherName()); | |
// 结果:Student (studentName=Tony, studentAge=14, className = 四年二班,teacherName=Kite, height=1.5, weight=50, homeAddress = 合肥市包河区) | |
System.out.println(tony.toString()); | |
} |
上面简单设置两个属性看上去还好,但是如果几十条甚至上百条属性要变,一个一个 set 看上去会很糟糕
下面我们对比一下几个第三方工具中对于属性 copy 的使用,可以借鉴一下:
import com.example.demo.java.other.domain.Student; | |
import com.example.demo.java.other.domain.Teacher; | |
import org.apache.commons.beanutils.BeanUtils; | |
import org.modelmapper.Conditions; | |
import org.modelmapper.ModelMapper; | |
import org.springframework.context.annotation.Bean; | |
//import org.springframework.beans.BeanUtils; | |
/** | |
* <p> | |
* <code>PropertiesCopyTest</code> | |
* </p> | |
* Description:java 对象拷贝 | |
* 注意事项: | |
* 使用 spring 的 BeanUtils 不用抛异常,而使用 Apache 的 BeanUtils 必须抛异常(反射),否则编译报错 | |
* 使用 BeanUtils 的 copyProperties 时,如果实体 B 想要 copy 实体 A,而实体 A 中某些和 B 相同的字段是 null 时, | |
* 也会被 copy 过去,导致实体 B 本身存在的数据丢失,此时可以考虑使用 modelMapper 工具 | |
* | |
* @author Mcchu | |
* @date 2017/10/13 12:07 | |
*/ | |
public class PropertiesCopyTest { | |
public static void main(String[] args) { | |
// Tony 是新转来的学生,还没有分配班级和老师,下面我们要将他分到和 mike 一个班 | |
Student mike = mike(); | |
Student tony = tony(); | |
// 1. 使用 spring 自带的工具,tony 的属性被全部覆盖了,这不是我们想要的 | |
//BeanUtils.copyProperties(mike,tony); | |
// 结果失败:Student (name=Mike, age=12, className = 四年二班,teacherName=Kite, height=1.4, weight=40, homeAddress = 合肥市蜀山区) | |
//System.out.println(tony.toString()); | |
try { | |
// 2. 使用 apache 工具,将属性一次性全部地 copy 到 tony 上,可以发现 tony 的属性被全部覆盖了,这也不是我们想要的 | |
//BeanUtils.copyProperties(tony,mike); | |
// 结果失败:Student (name=Mike, age=12, className = 四年二班,teacherName=Kite, height=1.4, weight=40, homeAddress = 合肥市蜀山区) | |
//System.out.println(tony.toString()); | |
// 3. 使用 apache 工具,退而求其次,我们将属性一个一个地 copy 到 tony 的属性上,这样是可以的 | |
//BeanUtils.copyProperty( tony, "className", mike.getClassName() ); | |
//BeanUtils.copyProperty( tony, "teacherName", mike.getTeacherName() ); | |
// 结果正确:Student (name=Tony, age=14, className = 四年二班,teacherName=Kite, height=1.5, weight=50, homeAddress = 合肥市包河区) | |
//System.out.println(tony.toString()); | |
}catch (Exception e){ | |
e.printStackTrace(); | |
} | |
// 4. 我们反过来想,如果将 tony 的属性先覆盖到 mike 的属性上,这里的覆盖有个关键性条件,即只覆盖存在数据的字段,如果为 null 时,不做覆盖操作,此时得到的 mike 属性不就是 tony 需要的真实属性吗? | |
//ModelMapper modelMapper = new ModelMapper(); | |
//modelMapper.getConfiguration().setPropertyCondition(Conditions.isNotNull()); | |
//modelMapper.map(tony,mike); | |
//tony = mike; | |
// 结果正确:Student (name=Tony, age=14, className = 四年二班,teacherName=Kite, height=1.5, weight=50, homeAddress = 合肥市包河区) | |
//System.out.println(tony.toString()); | |
// 5. 假如我们可以拿得到 kite 老师的属性,而老师的属性里又存在 tony 需要的 className 和 teacherName,那么我们直接 copyProperties 就好了 | |
try { | |
Teacher kite = kite(); | |
BeanUtils.copyProperties(tony,kite); // 此处等同于 org.springframework.beans.BeanUtils.copyProperties (kite,tony); | |
// 结果正确:Student (studentName=Tony, studentAge=14, className = 四年二班,teacherName=Kite, height=1.5, weight=50, homeAddress = 合肥市包河区) | |
System.out.println(tony.toString()); | |
}catch (Exception e){ | |
e.printStackTrace(); | |
} | |
} | |
private static Student mike(){ | |
return new Student("Mike",12,"四年二班","Kite","1.4","40","合肥市蜀山区"); | |
} | |
private static Student tony(){ | |
return new Student("Tony",14,null,null,"1.5","50","合肥市包河区"); | |
} | |
private static Teacher kite(){ | |
return new Teacher("Kite",36,"四年二班"); | |
} | |
} |
上面 5 种方式中,后 3 种是可以实现正确结果的
附录:Student 实体类和 Teacher 实体类
import lombok.AllArgsConstructor; | |
import lombok.Data; | |
import lombok.NoArgsConstructor; | |
/** | |
* <p> | |
* <code>Teacher</code> | |
* </p> | |
* Description: | |
* | |
* @author Mcchu | |
* @date 2017/10/13 13:34 | |
*/ | |
@Data | |
@AllArgsConstructor | |
@NoArgsConstructor | |
public class Teacher { | |
private String teacherName; | |
private Integer teacherAge; | |
private String className; | |
} |
import lombok.AllArgsConstructor; | |
import lombok.Data; | |
import lombok.NoArgsConstructor; | |
/** | |
* <p> | |
* <code>Student</code> | |
* </p> | |
* Description: | |
* | |
* @author Mcchu | |
* @date 2017/10/13 12:07 | |
*/ | |
@Data | |
@AllArgsConstructor | |
@NoArgsConstructor | |
public class Student { | |
private String studentName; | |
private Integer studentAge; | |
private String className; | |
private String teacherName; | |
private String height; | |
private String weight; | |
private String homeAddress; | |
} |