更多资源请访问 www.itkc8.com
BeanUtils.copyProperties()方法和PropertyUtils.copyProperties()的区别
首先两者来源于同一个包:
1 2 |
|
千万不要导包错误,否者会报异常。
BeanUtils提供对Java反射和自省API的包装。其主要目的是利用反射机制对JavaBean的属性进行处理。我们知道,一个JavaBean通常包含了大量的属性,很多情况下,对JavaBean的处理导致大量get/set代码堆积,增加了代码长度和阅读代码的难度。
以下是几点要求及说明:
1、 通过反射将一个对象的值赋值个另外一个对象(前提是对象中属性的名字相同)。
2、 BeanUtils.copyProperties(obj1,obj2); 经常闹混不知道是谁给谁赋值,无意中先到"后付前"这个词来帮助自己记忆这个功能。即将obj2的值赋值给obj1。
3、 如果2中实例obj2为空对象,即值new了他的实例并没有赋值的话obj1对应的属性值也会被设置为空置。
4、BeanUtils与PropertyUtils对比(这里对比copyProperties方法)
PropertyUtils的copyProperties()方法几乎与BeanUtils.copyProperties()相同,主要的区别在于后者提供类型转换功能,即发现两个JavaBean的同名属性为不同类型时,在支持的数据类型范围内进行转换,PropertyUtils不支持这个功能,所以说BeanUtils速度会更快一些,使用更普遍一点,犯错的风险更低一点。
举个例子:创建两个对象,然后 给一个对象赋值 最后把已经赋值的对象 copy到另一个空对象里面
首先创建Person对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
|
然后 创建Student 对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
|
然后创建测试类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
执行以上方法会打印出:
1 |
|
在执行 BeanUtils.copyProperties(student, person)时 如果把student对象的age 类型换成 String 也是可以打印出正常的结果。但是PropertyUtils.copyProperties(student, person)就会抛出异常。
------------------
使用Mapstruct来进行PO与VO之间的映射
区别与mvc模型的 mvvm模型,将模型对象与视图对象view model分离开,来做到与底层model分离开来。大大解耦底层model与界面vo的关系,至此就需要一个工具来做到po与vo分离开来。
最初的想法是使用apache-beanutils 但是其对一些深层次对象拷贝做不到,虽然可以通过改写其内部源码实现对嵌套对象属性拷贝,但是出现特殊业务转换,如 属性名字不匹配,beanutils对于mapstruct就相形见绌了。
话不多说:
maven引入
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.1.0.Final</version>
</dependency>
模拟po对象
public class Person {
private String name;
private Integer age;
private Date birthdate;
private float wallet;
...setter getter
}
创建vo对象
public class PersonVo {
private String name;
private Integer age;
private Date birth;//与po对象属性名不一致
private float wallet;
private String birthformat;//通过po对象的某一属性扩展
...setter getter
}
创建mapstuct 接口
@Mapper
public interface Persion2PersonVoMapper {
Persion2PersonVoMapper MAPPER = Mappers.getMapper(Persion2PersonVoMapper.class);
@Mappings({
@Mapping(source = "birthdate", target = "birth"),//属性名不一致映射
@Mapping(target = "birthformat", expression = "java(org.apache.commons.lang3.time.DateFormatUtils.format(person.getBirthdate(),\"yyyy-MM-dd HH:mm:ss\"))"),//自定义属性通过java代码映射
})
public PersonVo PersonToPersonVo(Person person);
public List<PersonVo> PersonToPersonVos(List<Person> list);
}
创建测试类
public class MapStuctTest {
//单个对象映射
@Test
public void singleTest(){
Person person=new Person("wayne",12,new Date(),12f);
PersonVo personVo = Persion2PersonVoMapper.MAPPER.PersonToPersonVo(person);
Assert.assertTrue(personVo.getName().equals(person.getName()));
Assert.assertTrue(personVo.getAge().equals(person.getAge()));
Assert.assertTrue(personVo.getBirth().equals(person.getBirthdate()));
Assert.assertTrue(personVo.getWallet()==person.getWallet());
}
//对象集合映射
@Test
public void listTest(){
Person person=new Person("wayne",12,new Date(),12f);
Person person2=new Person("wayne2",13,new Date(new Date().getTime()+3600000),13f);
Person person3=new Person("wayne3",14,new Date(new Date().getTime()+7200000),14f);
Person person4=new Person("wayne4",15,new Date(new Date().getTime()+9800000),15f);
List<Person> list=new ArrayList();
list.add(person);
list.add(person2);
list.add(person3);
list.add(person4);
List<PersonVo> personVos = Persion2PersonVoMapper.MAPPER.PersonToPersonVos(list);
Assert.assertTrue(list.size()==personVos.size());
}
}
我们来看看debug信息。
1. 单个对象映射测试
singleTest()测试方法
查看debug信息
使用一行 语句
PersonVo personVo = Persion2PersonVoMapper.MAPPER.PersonToPersonVo(person);
1
源属性 birthdate 已经转换到 目标属性 birth,同时我们目标类的自定义属性也接收到源对象的格式化 birthdate 属性值为“2017-05-28 13:22:21”
单个对象并没有体现便捷,我们来看集合对象的转换。
2. 集合对象映射测试
listTest()测试方法
查看debug信息
这是源对象集合信息
映射后的目标集合对象信息
可以看到集合转集合也只使用一行代码
List<PersonVo> personVos = Persion2PersonVoMapper.MAPPER.PersonToPersonVos(list);
1
帮我们省去很多集合遍历添加操作。
ok 我们来看看底层原理。
我们所写的 Persion2PersonVoMapper 接口在编译时生成一个Persion2PersonVoMapperImpl 实现类。
public class Persion2PersonVoMapperImpl implements Persion2PersonVoMapper {
@Override
public PersonVo PersonToPersonVo(Person person) {
if ( person == null ) {
return null;
}
PersonVo personVo = new PersonVo();
personVo.setBirth( person.getBirthdate() );
personVo.setName( person.getName() );
personVo.setAge( person.getAge() );
personVo.setWallet( person.getWallet() );
personVo.setBirthformat( org.apache.commons.lang3.time.DateFormatUtils.format(person.getBirthdate(),"yyyy-MM-dd HH:mm:ss") );
return personVo;
}
@Override
public List<PersonVo> PersonToPersonVos(List<Person> list) {
if ( list == null ) {
return null;
}
List<PersonVo> list_ = new ArrayList<PersonVo>();
for ( Person person : list ) {
list_.add( PersonToPersonVo( person ) );
}
return list_;
}
}
我并未对生成类进行修改,只稍作格式化。
原来 并没有很高级的操作,只是mapstruct 帮我们生成的 手动get set操作,然后for add操作。
自定义转义
对一些特殊的转换 我们可以进行自定义订制。
public class MapStructUtils {
public float asfloat(float inputfloat){
BigDecimal bigDecimal =new BigDecimal(inputfloat+"");
return bigDecimal.setScale(2,BigDecimal.ROUND_HALF_UP).floatValue();
//float数据 四舍五入保留两位小数
}
}
mapper上面添加注解
@Mapper(uses=MapStructUtils.class)
1
查看对应的生成实现
@Override
public PersonVo PersonToPersonVo(Person person) {
if ( person == null ) {
return null;
}
PersonVo personVo = new PersonVo();
personVo.setBirth( person.getBirthdate() );
if ( person.getBirthdate() != null ) {
personVo.setBirthformat( new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" ).format( person.getBirthdate() ) );
}
personVo.setName( person.getName() );
personVo.setAge( person.getAge() );
personVo.setWallet( mapStructUtils.asfloat( person.getWallet() ) );
return personVo;
}
可以查看到 float类型转换时 使用了我们自定义的方法
personVo.setWallet( mapStructUtils.asfloat( person.getWallet() ) );
1
vo转换为po Inverse反转
反转映射
Person2PersonVoMapper.java
@InheritInverseConfiguration
public Person PersonVoToPerson(PersonVo personVo);
测试类
@Test
public void singleTest(){
Person person=new Person("wayne",12,new Date(),12.1231231f);
PersonVo personVo = Person2PersonVoMapper.MAPPER.PersonToPersonVo(person);
Assert.assertTrue(personVo.getName().equals(person.getName()));
Assert.assertTrue(personVo.getAge().equals(person.getAge()));
Assert.assertTrue(personVo.getBirth().equals(person.getBirthdate()));
//Inverse 反转
personVo.setBirthformat("2017-05-28 19:59:27");
Person person1 = Person2PersonVoMapper.MAPPER.PersonVoToPerson(personVo);
}
vo反向生成po对象
vo更新po
@Mappings({
@Mapping(target = "birthdate", source = "birth")
})
public void UpdatePersonVo( PersonVo personVo, @MappingTarget Person person);
单元测试
@Test
public void singleUpdateTest(){
Person person=new Person("wayne",12,new Date(),12f);
PersonVo personVo = Persion2PersonVoMapper.MAPPER.PersonToPersonVo(person);
//Person person2=new Person("handsome wayne",92,new Date(new Date().getTime()+9800000),92f);
personVo.setName("handsome wayne");
personVo.setAge(92);
personVo.setBirth(new Date(new Date().getTime()+9800000));
Persion2PersonVoMapper.MAPPER.UpdatePersonVo(personVo,person);
}
反向更新
map映射
@MapMapping(valueDateFormat ="yyyy-MM-dd HH:mm:ss")
public Map<String ,String> DateMapToStringMap(Map<String,Date> sourceMap);
1
2
测试方法
@Test
public void mapMappingTest(){
Map<String,Date> map=new HashMap<>();
map.put("key1",new Date());
map.put("key2",new Date(new Date().getTime()+9800000));
Map<String, String> stringObjectMap = Person2PersonVoMapper.MAPPER.DateMapToStringMap(map);
}
总结一下
关于mapper接口
它可以自动封装一些同级,属性名相同的属性名如上name age 属性,可以无需手动写 @Mapping
对于非同级或属性名 需要写相关的 @mapping 属性名不同可以查考 birth 属性配置。如果不同级@Mapping(source = “XXX.birthdate”, target = “XX.birth”) 如此就行了。
关于实现类,老版本的eclipse可能无法自动编译出来,如果使用maven,install就可以有生产类,以防可能会出现 class not found 异常。
@mapping 还有很多属性
public @interface Mapping {
String target();
String source() default "";
String dateFormat() default "";
String numberFormat() default "";
String constant() default "";
String expression() default "";
boolean ignore() default false;
Class<? extends Annotation>[] qualifiedBy() default {};
String[] qualifiedByName() default {};
Class<?> resultType() default void.class;
String[] dependsOn() default {};
String defaultValue() default "";
}
target,source,expression 在此不再复述,
dateFormat 可以代替事例中expression ,如此:
@Mapping(target = "birthformat", source = "birthdate",dateFormat = "yyyy-MM-dd HH:mm:ss"),
//生产类
if ( person.getBirthdate() != null ) {
personVo.setBirthformat( new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" ).format( person.getBirthdate() ) );
}
---------------------
如果模型与实体均存在很多属性的情况下,映射接口的@Mapping注解很容易写得很长,比如:
[java] view plain copy
- @Mapper
- public interface PersonMapper {
- PersonMapper INSTANCE = Mappers.getMapper(PersonMapper.class);
- @Mapping(target = "personName", source = "name")
- @Mapping(target = "hand", ignore = true)
- ..
- ..
- ..
- ..
- PersonModel map( Personentity);
- List<PersonModel > map(List< Person> entity);
- }
这样的程序就不可避免的写得很笨了。所以我们也可以使用default默认方法来定义映射接口,如:
[java] view plain copy
- @Mapper
- public interface PersonMapper {
- PersonMapper INSTANCE = Mappers.getMapper(PersonMapper.class);
- @Mapping(target = "hand", ignore = true)
- PersonModel map(Person entity, Person personName, Person age, Person phone);
- default PersonModel map(Person entity) {
- return INSTANCE.map(entity, entity.getName(), entity.getAge(), entity.getPhone());
- }
- List<PersonModel> map(List<Person> entity);
- }
<2>使用静态类映射实体
[java] view plain copy
- @Mapper(componentModel = "spring")
- public abstract class PesonMapper {
- @Mapping(target = "personName", source = "name")
- @Mapping(target = "hand", ignore = true)
- protected abstract PersonModel map( Person entity);
- protected abstract List<PersonModel > map(List< Person> entity);
- }
虽然看起来写法差不多一直,但是使用静态类来映射有他的好处,最起码接口只能定义方法,无法写方法体,但是使用了静态类,就可以写上方法体了,比如:
[java] view plain copy
- @Mapper(componentModel = "spring")
- public abstract class PesonMapper {
- public PersonModel mapEighteen( Person entity) {
- if (entity.getName.equals(18)) {
- return map(entity);
- }
- };
- @Mapping(target = "personName", source = "name")
- @Mapping(target = "hand", ignore = true)
- protected abstract PersonModel map( Person entity);
- protected abstract List<PersonModel > map(List< Person> entity);
- }
调用mapEighteen()就可以只映射实体属性age为18的实体了。