官网案例
以下将官网案例做一个解释
1)快速入门
递归遍历源对象的属性拷贝给目标对象
拷贝对象下对象的属性值
@Data
class Order {
private Customer customer;
private Address billingAddress;
}
@Data
class Customer {
private Name name;
}
@Data
class Name {
private String firstName;
private String lastName;
}
@Data
class Address {
private String street;
private String city;
}
@Data
class OrderDTO {
private String customerFirstName;
private String customerLastName;
private String billingStreet;
private String billingCity;
}
public class Test {
public static void main(String[] args) {
Order order = new Order();
Customer zww = new Customer();
Name name = new Name();
Address address = new Address();
// OrderDTO orderDTO = new OrderDTO();
name.setFirstName("ww");
name.setLastName("Z");
zww.setName(name);
address.setCity("汕头");
address.setStreet("明茵路");
order.setCustomer(zww);
order.setBillingAddress(address);
System.out.println(order);
ModelMapper modelMapper = new ModelMapper();
OrderDTO orderDTO = modelMapper.map(order, OrderDTO.class);
System.out.println(orderDTO);
}
}
通过上述例子,我们可以将order中的customer对象中的name中的firstname拷贝到orderDto的firstname
匹配规则是根据属性名拼接,例如customerFirstname就是找customer属性对象,然后下一层的firstname,但是没有,找name的下一层,有firstname,根据属性名的映射递归一个对象的子对象,直至找到属性如果属性名
匹配属性名从第一个字符开始匹配,如果匹配成功跳出,以至于为什么firstNameXXXdfsf能命中,但是如果是下面的情况就匹配不上
1.1)解释
比较源和目标类型, 根据匹配策略(后续自定义匹配器) 如果没定义策略, 会尽可能的匹配, 例如上述提到的后面加XXX和前面加XXX产生属性名不一致的匹配情况
我们也可以设置匹配策略为松散的getConfiguration。setMatchingStrategy(常量LOOSE),这样属性名前几个字符就算不一致 也能匹配上,street。billingStreet
或者是设置一个验证, 或者添加映射规则 addmappings
2)属性映射
1.传入源值和目标值,返回一个typeMap,刚才是直接map映射且返回对象,此时我们不急着map,自定义一些规则
2.我们这里新增一个目标属性来进行测试
3.这里我们使用了一个addMapping添加映射规则,参数1提取源对象的属性,参数2设置属性映射给其他对象,
如果层级只有一层,可以使用方法引用,XXX::getXXX不过如果你要映射的属性在对象里的一个对象属性里面。那就不能XXX:getXXX::getAAA链式方法引用了。
参数2你也可以使用箭头函数(lambda表达式 x->x.getxxx 隐式返回x对象,类似于js)传入目标元素dest,和从第一个参数获取到的value值(默认为Object类型,且如果是字符串不能toString而是强转为String -> (String)value)。然后进行setXXX((String)value)
最后我们仅需要,调用map方法开始映射。返回值就是映射也可以说是转换后的新对象。效果如上, 官网说明如下,代码结尾再声明先展示效果,后续需要CV自己运行查看即可
但是如果你是使用的 lambda表达式,转换类型只需要
前面统一规定即可
4.除了这种addMapping还可以使用lambda添加多个规则/ 或者映射属性到另一个不同类型的属性上
5.深度映射
说白了就是第一个参数和第二个参数用lambda一个一个get进去,或者get到里面再set而已
6.跳过映射,排除一些属性不拷贝
规则器上调用方法传输 目标的方法引用
3)变换器
有点类似于插件,会对映射后的值做一个统一处理,例如这里我们要把映射后的值全部变为大写
我们看一下官方文档如何解释的
①首先用一个lambda 实现了函数式接口,什么是函数式接口?也就是一个接口里只能有一个抽象方法,和若干个静态或者默认方法,用lambda实现,函数式接口接收该实现返回的接口后续调用该接口的实现方法
也可以实现抽象方法的基础上再调用默认方法
如果函数式接口有多个 抽象方法,那么该接口就不能作为函数式接口
②using使用该函数式接口
这个converter第一个泛型为string为src源属性值,第二个也是string,目标属性值
我们已经给这个函数式接口实现了convert方法,using即可,然后他会在映射给dest目标元素时将src做一个加工处理,调用函数式接口toUpper里的我们实现的lambda表达式方法covert传入上下文context也就是ctx然后获取源值getSource,加工后返回出来的新的src,最后映射给dest类。效果如下
③lambda定义转换器
官网这里没做空值判断,我们给他加一个,和前面统一一点
看到另一个博主做了一个枚举转换
只要搞清楚一点点原理,就比较好懂了,这里用到了lambda传入上下文,返回一个枚举类, 这个枚举类通过getInstance 传入source字符串的构造函数生成,然后映射到OrderDTO的orderSource属性上,so ez有没有,只要设置好函数式接口的入类型String和出类型 OrderSource,然后获取上下文的source对齐进行加工就可以得到美味蟹黄堡了奥里给
4)Provider设置默认值
当映射属性的源值为null时,可以指定一个默认值,外部定义or lambda表达式
先不设置值,然后调用查看有无默认值
5)条件判断器
同理,外部定义还是lambda,累了不说了
由于是空,不映射
当然也可以不用自己实现,有内置方法的提供
官网还有具体映射的过程,这里就不看了,有兴趣的小伙伴可以自行研究,以上的用法够用了感觉
代码下面的为个人前言,有点长,不想看可以退了
代码
@Data
class Order {
private Customer customer;
private Address billingAddress;
}
@Data
class Customer {
private Name name;
}
@Data
class Name {
private String xxfirstName;
private String lastName;
}
@Data
class Address {
private String street;
private String city;
}
@Data
class OrderDTO {
private String customerFirstName;
private String customerLastName;
private String billingStreet;
private String billingCity;
private String testAddMapping;
}
public class Test {
public static void main(String[] args) {
Order order = new Order();
Customer zww = new Customer();
Name name = new Name();
Address address = new Address();
// OrderDTO orderDTO = new OrderDTO();
// name.setXxfirstName("ww");
name.setLastName("Z");
zww.setName(name);
address.setCity("汕头");
address.setStreet("明茵路");
order.setCustomer(zww);
order.setBillingAddress(address);
System.out.println("映射前:"+order);
ModelMapper modelMapper = new ModelMapper();
// 创建 TypeMap
TypeMap<Order, OrderDTO> typeMap = modelMapper.typeMap(Order.class, OrderDTO.class);
// // 1.单个mapping规则
// typeMap.addMapping(src->src.getCustomer().getName().getXxfirstName(),(dest,value)->dest.setTestAddMapping((String) value));
// typeMap.addMapping(src->src.getCustomer().getName().getXxfirstName(),OrderDTO::setTestAddMapping);
// // 2.lambda定义多个规则
// typeMap.addMappings(mapper->
// mapper.<String>map(src->src.getCustomer().getName().getXxfirstName(),(dest,value)->dest.setTestAddMapping(value)));
// typeMap.addMappings(mapper->
// {
// mapper.map(src->src.getCustomer().getName().getXxfirstName(),(dest,value)->dest.setTestAddMapping((String) value));
// mapper.map(src->src.getCustomer().getName().getXxfirstName(),(dest,value)->dest.setTestAddMapping((String) value));
// mapper.map(src->src.getCustomer().getName().getXxfirstName(),(dest,value)->dest.setTestAddMapping((String) value));
// });
// // 3.跳过映射
// typeMap.addMappings(mapper->mapper.skip(OrderDTO::setCustomerLastName));
// 4.变换器
// Converter<String, String> toUppercase =
// ctx -> ctx.getSource() == null ? null : ctx.getSource().toUpperCase();
// typeMap.addMappings(mapper->{
// mapper.using(toUppercase).<String>map(src->src.getCustomer().getName().getXxfirstName(),(dest,value)->dest.setTestAddMapping(value));
// });
// // 4.1 lambda定义变换器
// typeMap.addMappings(mapper->{
// mapper.using(ctx ->(ctx.getSource()==null?null:(String)(ctx.getSource())).toUpperCase())
// .<String>map(src->src.getCustomer().getName().getXxfirstName(),(dest,value)->dest.setTestAddMapping(value));
// });
// // 5.provider默认值
// typeMap.addMappings(mappper->{
// mappper.with((Provider<String> )p->{return "哈哈哈";}).<String>map(src->src.getCustomer().getName().getXxfirstName(),(dest,value)->dest.setTestAddMapping(value));
// });
// 6.条件构造器
typeMap.addMappings(mapper->{
mapper.when((Condition<String,String>)c->c.getSource()!=null&&!c.getSource().isEmpty()).<String>map(src->src.getCustomer().getName().getXxfirstName(),(dest, value)->dest.setTestAddMapping(value));
});
// 执行映射
OrderDTO map = typeMap.map(order);
System.out.println("映射后:"+map);
}
}
前言
在一个开源项目中看到的手法,貌似是beanutils的升级版
感觉很牛逼的一个东西,
场景举例
前端传来的对象M里有一个List和共同的name属性 根据前端的坑挖的属性名
而后端不需要这个List而是需要将A变成B,需要将List转为List,
第一个简称bm,第二个ob(object)
如何将ListVM里的 List变成List<List
自己的做法
AtomicInteger index = new AtomicInteger(1);
List<PaperQuestionTypeDto> paperQuestionTypeDto = paperDto.getPaperQuestionTypeDto();
// 这一步将前端的题型类转为后端的题型类,也就是其里面的题目类改造为order和id的
List<PaperQuestionTypeVM> paperQuestionTypeVMList = paperQuestionTypeDto.stream().map(i -> {
// 后端题型类
PaperQuestionTypeVM paperQuestionTypeVM = new PaperQuestionTypeVM();
//改造题目类
List<PaperQuestionVM> collect = i.getQuestionDtos().stream().map(p -> {
PaperQuestionVM paperQuestionVM = new PaperQuestionVM();
paperQuestionVM.setId(p.getId());
paperQuestionVM.setItemOrder(index.getAndIncrement());
return paperQuestionVM;
}).collect(Collectors.toList());
// 初始化name值
paperQuestionTypeVM.setName(i.getName());
paperQuestionTypeVM.setPaperQuestionVMS(collect);
return paperQuestionTypeVM;
}).collect(Collectors.toList());
参照他人做法,先引入个依赖
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>2.3.3</version>
</dependency>
配置类
package com.mindskip.xzs.utility;
import org.modelmapper.ModelMapper;
import org.modelmapper.convention.MatchingStrategies;
public class ModelMapperSingle {
/**
* The constant modelMapper.
*/
protected final static ModelMapper modelMapper = new ModelMapper();
private final static ModelMapperSingle modelMapperSingle = new ModelMapperSingle();
static {
modelMapper.getConfiguration().setFullTypeMatchingRequired(true);
modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
}
/**
* Instance model mapper.
*
* @return the model mapper
*/
public static ModelMapper Instance() {
return modelMapperSingle.modelMapper;
}
}
静态生成单例,加到静态方法工厂后续调用获取对象
引入并书写方法
protected final static ModelMapper modelMapper = ModelMapperSingle.Instance();
AtomicInteger index = new AtomicInteger(1);
// 这一步将前端的题型类转为后端的题型类,也就是其里面的题目类改造为order和id的
// 方法2,使用modelMapper映射对象数据不使用set 和new直接比对相同属性名,拷贝属性和值
List<PaperQuestionTypeVM> paperQuestionTypeVMList = paperQuestionTypeDto.stream().map(i -> {
// 这一步将内部列表映射到另一个list,只映射属性名一致的值和属性,同步了name,省去new,setName
PaperQuestionTypeVM questionTypeVM = modelMapper.map(i, PaperQuestionTypeVM.class);
List<PaperQuestionVM> questionVMS = i.getQuestionDtos().stream().map(q -> {
// 将题目列表转为别的类,仅包含order和id,这里id属性一致,映射,order自己set
PaperQuestionVM questionVM = modelMapper.map(q, PaperQuestionVM.class);
questionVM.setItemOrder(index.getAndIncrement());
return questionVM;
}).collect(Collectors.toList());
// 将遍历的出来新的的题目列表赋值给题型.name不用给了,映射好了
questionTypeVM.setPaperQuestionVMS(questionVMS);
return questionTypeVM;
}).collect(Collectors.toList());
先map List将其每一个A转为B,然后再对里面的ListC遍历将C转为只有order和id的D类,返回为ListD,将ListD设置给ListB,完成了List(ListC) 为List (List)
少了一个setId和setName,因为属性名一致,直接映射,有点类似于beanutils的copy。不过这个还可以自定义映射的属性,source的dest目的属性,做一个匹配
ModelMapper modelMapper = new ModelMapper();
// 自定义映射规则
modelMapper.typeMap(UserDTO.class, User.class).addMappings(mapper -> {
mapper.map(UserDTO::getUsername, User::setName); // 映射用户名
mapper.map(src -> src.getAddress().getStreet(), (dest, value) -> dest.getUserAddress().setStreetName((String) value)); // 嵌套映射
mapper.map(src -> src.getAddress().getCity(), (dest, value) -> dest.getUserAddress().setCityName((String) value)); // 嵌套映射
});
// 执行映射
User user = modelMapper.map(userDTO, User.class);
从一个类的属性映射到另一个类的属性上
试试看用beanutils
private static List<PaperQuestionTypeVM> getPaperQuestionTypeVMS(List<PaperQuestionTypeDto> paperQuestionTypeDto) {
AtomicInteger index = new AtomicInteger(1);
// 这一步将前端的题型类转为后端的题型类,也就是其里面的题目类改造为order和id的
// 方法2,使用modelMapper映射对象数据不使用set 和new直接比对相同属性名,拷贝属性和值
List<PaperQuestionTypeVM> paperQuestionTypeVMList = paperQuestionTypeDto.stream().map(i -> {
// 这一步将内部列表映射到另一个list,只映射属性名一致的值和属性,同步了name,省去new,setName
// PaperQuestionTypeVM questionTypeVM = modelMapper.map(i, PaperQuestionTypeVM.class);
PaperQuestionTypeVM paperQuestionTypeVM = new PaperQuestionTypeVM();
BeanUtils.copyProperties(i,paperQuestionTypeVM);
List<PaperQuestionVM> questionVMS = i.getQuestionDtos().stream().map(q -> {
// 将题目列表转为别的类,仅包含order和id,这里id属性一致,映射,order自己set
// PaperQuestionVM questionVM = modelMapper.map(q, PaperQuestionVM.class);
PaperQuestionVM paperQuestionVM = new PaperQuestionVM();
BeanUtils.copyProperties(q,paperQuestionVM);
paperQuestionVM.setItemOrder(index.getAndIncrement());
return paperQuestionVM;
}).collect(Collectors.toList());
// 将遍历的出来新的的题目列表赋值给题型.name不用给了,映射好了
paperQuestionTypeVM.setPaperQuestionVMS(questionVMS);
return paperQuestionTypeVM;
}).collect(Collectors.toList());
return paperQuestionTypeVMList;
}
不过这样还要多一步new出来然后再copy的操作,那个直接一步到位,copy并且new
怀着好奇心打开了官网,学习一下具体其他用法
其他用法
作用:PO(persistence object持久层也就是对应数据库列的类) 与BO(Bussiness service之间的类) 与VO(value Object 后端给前端的那玩意)和DTO(data transfer object 前端表单的字段集合那个formData) 之间的相互转换,属性拷贝