参考博客:
基于Spring BeanUtils 实现 JavaBean克隆及属性拷贝
基于apache BeanUtils 实现 JavaBean克隆及属性拷贝
基于 MapStruct实现 JavaBean克隆及属性拷贝
Intellij 插件 GenerateO2O
MapStruct简介 简介- 性能高 这是相对反射来说的,反射需要去读取字节码的内容,花销会比较大。而通过 MapStruct 来生成的代码,其类似于人手写。速度上可以得到保证。
- 用简单 如果是完全映射的,使用起来肯定没有反射简单。用类似 BeanUtils 这些工具一条语句就搞定了。但是,如果需要进行特殊的匹配(特殊类型转换,多对一转换等),其相对来说也是比较简单的。 基本上,使用的时候,我们只需要声明一个接口,接口下写对应的方法,就可以使用了。当然,如果有特殊情况,是需要额外处理的。推荐:Java进阶视频资源
- 代码独立 生成的代码是对立的,没有运行时的依赖。
- 易于 debug 在我们生成的代码中,我们可以轻易的进行 debug。
org.mapstruct
mapstruct
1.4.2.Final
org.projectlombok
lombok
1.18.22
provided
com.github.houbb
data-factory-core
1.1.0
pom.xml中添加插件支持
(重点,不要忘了)
maven-compiler-plugin
3.8.0
11
11
org.mapstruct
mapstruct-processor
1.4.2.Final
org.projectlombok
lombok
1.18.22
org.projectlombok
lombok-mapstruct-binding
0.2.0
入门示例:属性名相同、属性个数不同
待转换的类
@Getter
@Setter
@ToString
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
private static final long serialVersionUID = 6357528845064191241L;
/**
* 编号
*/
private Long id;
/**
* 昵称
*/
private String nickname;
/**
* 大头相
*/
private String avatar;
/**
* 登录名
*/
private String account;
/**
* 密码
*/
private String password;
/**
* 性别 1男 2女 3未知
*/
private Byte gender;
/**
* 电话
*/
private String tel;
/**
* 邮编
*/
private String email;
/**
* QQ
*/
private String qq;
/**
* 微信
*/
private String wechat;
/**
* 盐值
*/
private String salt;
/**
* 备注
*/
private String info;
/**
* 状态
*/
private Byte status;
/**
* 注册时间
*/
private Date createTime;
/**
* 更新时间
*/
private Date updateTime;
}
目标类
@Getter
@Setter
@ToString
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class UserBO implements Serializable {
private static final long serialVersionUID = 7257164843346458694L;
/**
* 编号
*/
private Long id;
/**
* 昵称
*/
private String nickname;
/**
* 大头相
*/
private String avatar;
/**
* 邮编
*/
private String email;
/**
* 微信
*/
private String wechat;
/**
* 状态
*/
private Byte status;
}
转换接口
@Mapper
public interface UserMapper {
UserMapper MAPPER = Mappers.getMapper(UserMapper.class);
UserBO user2UserBO(User user);
List user2UserBO(List user);
}
测试代码
public class MapStructTest {
@Test
public void fun1() {
User user = DataUtil.build(User.class);
System.out.println(user);
UserBO userBO = UserMapper.MAPPER.user2UserBO(user);
System.out.println(userBO);
}
@Test
public void fun2() {
List list = new ArrayList();
list.add(DataUtil.build(User.class));
list.add(DataUtil.build(User.class));
list.add(DataUtil.build(User.class));
list.forEach(System.out::println);
List res = UserMapper.MAPPER.user2UserBO(list);
res.forEach(System.out::println);
}
}
注:UserMapper只是一个接口,它只所以能够直接使用,是因为系统自动生成
了UserMapper的实现类,具体代码如下所示:
public class UserMapperImpl implements UserMapper {
public UserMapperImpl() {
}
public UserBO user2UserBO(User user) {
if (user == null) {
return null;
} else {
UserBOBuilder userBO = UserBO.builder();
userBO.id(user.getId());
userBO.nickname(user.getNickname());
userBO.avatar(user.getAvatar());
userBO.email(user.getEmail());
userBO.wechat(user.getWechat());
userBO.status(user.getStatus());
return userBO.build();
}
}
public List user2UserBO(List user) {
if (user == null) {
return null;
} else {
List list = new ArrayList(user.size());
Iterator var3 = user.iterator();
while(var3.hasNext()) {
User user1 = (User)var3.next();
list.add(this.user2UserBO(user1));
}
return list;
}
}
}
进阶一:属性名不同
待转换的类
@Getter
@Setter
@ToString
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
private static final long serialVersionUID = 6357528845064191241L;
/**
* 编号
*/
private Long id;
/**
* 昵称
*/
private String nickname;
/**
* 大头相
*/
private String avatar;
/**
* 邮箱
*/
private String email;
/**
* 微信
*/
private String wechat;
/**
* 更新时间
*/
private Date updateTime;
}
目标类
@Getter
@Setter
@ToString
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class UserBO implements Serializable {
private static final long serialVersionUID = 7257164843346458694L;
/**
* 编号
*/
private Long id;
/**
* 昵称
*/
private String nickname;
/**
* 大头相
*/
private String img;
/**
* 邮编
*/
private String email;
/**
* 微信
*/
private String wexin;
/**
* 更新时间
*/
private Date updateTime22;
}
转换接口
@Mapper
public interface UserMapper {
UserMapper MAPPER = Mappers.getMapper(UserMapper.class);
@Mappings({
@Mapping(source = "avatar", target = "img"),
@Mapping(source = "wechat", target = "wexin"),
@Mapping(source = "updateTime", target = "updateTime22")
})
UserBO user2UserBO(User user);
}
测试类
public class MapStructTest {
@Test
public void fun() {
User user = DataUtil.build(User.class);
System.out.println(user);
UserBO userBO = UserMapper.MAPPER.user2UserBO(user);
System.out.println(userBO);
}
}
进阶二:数据类型不一样
待转换的类
@Getter
@Setter
@ToString
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class User {
/**
* 编号
*/
private Long id;
/**
* 昵称
*/
private String nickname;
/**
* 创建时间
*/
private LocalDateTime createTime;
}
目标类
@Getter
@Setter
@ToString
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class UserBO {
/**
* 编号
*/
private Long id;
/**
* 昵称
*/
private String nickname;
/**
* 创建时间
*/
private String createTime;
}
转换工具类
public class DateTransUtil {
public static LocalDateTime str2LocalDateTime(String str){
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
return LocalDateTime.parse(str,dtf);
}
public static String localDateTime2Str(LocalDateTime localDateTime){
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
return dtf.format(localDateTime);
}
}
转换接口
@Mapper
public interface UserMapper {
UserMapper MAPPER = Mappers.getMapper(UserMapper.class);
@Mapping(target = "createTime", expression = "java(com.hc.util.DateTransUtil.localDateTime2Str(user.getCreateTime()))")
UserBO user2UserBO(User user);
}
测试类
public class MapStructTest {
@Test
public void fun() {
User user = DataUtil.build(User.class);
System.out.println(user);
UserBO userBO = UserMapper.MAPPER.user2UserBO(user);
System.out.println(userBO);
}
}
进阶三:将多个对象转换成一个对象
待合并的类
- UserBO
@Getter
@Setter
@ToString
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class UserBO {
/**
* 编号
*/
private Long id;
/**
* 昵称
*/
private String nickname;
}
- AddressBO
@Getter
@Setter
@ToString
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class AddressBO {
private String addr;
}
目标类
@Getter
@Setter
@ToString
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class User {
/**
* 编号
*/
private Long id;
/**
* 昵称
*/
private String nickname;
/**
* 地址
*/
private String addr;
}
转换接口
@Mapper
public interface UserMapper {
UserMapper MAPPER = Mappers.getMapper(UserMapper.class);
@Mappings({
@Mapping(source = "userBO.id", target = "id"), //可以省略
@Mapping(source = "userBO.nickname", target = "nickname"), //可以省略
@Mapping(source = "addressBO.addr", target = "addr"), //可以省略
})
User user2UserBO(UserBO userBO, AddressBO addressBO);
}
说明:
- 当多个对象中, 有其中一个为 null, 则会直接返回 null
- 当多个原对象中,有相同名字的属性时,需要通过 @Mapping 注解来具体的指定, 以免出现歧义(不指定会报错)
public class MapStructTest {
@Test
public void fun() {
UserBO userBO = DataUtil.build(UserBO.class);
System.out.println(userBO);
AddressBO addressBO = DataUtil.build(AddressBO.class);
System.out.println(addressBO);
User user = UserMapper.MAPPER.user2UserBO(userBO,addressBO);
System.out.println(user);
}
}
进阶二:转换非基础类型的属性
待转换的类
目标类
转换接口
测试类
进阶二:更新Bean对象–基于泛型的转换(★★★★★)
待转换的实体类
@Getter
@Setter
@ToString
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
private static final long serialVersionUID = 6357528845064191241L;
/**
* 编号
*/
private Long id;
/**
* 昵称
*/
private String nickname;
/**
* 大头相
*/
private String avatar;
/**
* 登录名
*/
private String account;
/**
* 密码
*/
private String password;
/**
* 性别 1男 2女 3未知
*/
private Byte gender;
/**
* 电话
*/
private String tel;
/**
* 邮编
*/
private String email;
/**
* QQ
*/
private String qq;
/**
* 微信
*/
private String wechat;
/**
* 盐值
*/
private String salt;
/**
* 备注
*/
private String info;
/**
* 状态
*/
private Byte status;
/**
* 注册时间
*/
private Date createTime;
/**
* 更新时间
*/
private Date updateTime;
}
目标实体类
@Getter
@Setter
@ToString
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class UserBO implements Serializable {
private static final long serialVersionUID = 7257164843346458694L;
/**
* 编号
*/
private Long id;
/**
* 昵称
*/
private String nickname;
/**
* 大头相
*/
private String avatar;
/**
* 邮编
*/
private String email;
/**
* 微信
*/
private String wechat;
}
泛型类
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class PageBean {
/**
* 每页显示的条数
*/
private Integer pageSize = 10;
/**
* 当前的页码
*/
private Integer pageNum;
/**
* 一共有多少条记录
*/
private Long total;
/**
* 一共有多少页
*/
private Integer pages;
/**
* 每一页所显示的数据
*/
private List result;
/**
* 分页请求路径
*/
private String url;
}
转换接口
@Mapper
public interface UserMapper {
UserMapper MAPPER = Mappers.getMapper(UserMapper.class);
//方法一
PageBean user2UserBO(PageBean user);
//方法二
void user2UserBO(PageBean userPageBean, @MappingTarget PageBean userBOPageBean);
}
测试类
public class MapStructTest {
@Test
public void fun1() {
List list = new ArrayList();
list.add(DataUtil.build(User.class));
list.add(DataUtil.build(User.class));
list.add(DataUtil.build(User.class));
PageBean userPageBean = new PageBean();
userPageBean.setPages(10);
userPageBean.setPageNum(5);
userPageBean.setPageSize(8);
userPageBean.setResult(list);
userPageBean.setTotal(86L);
userPageBean.setUrl("getUserByCondition/");
PageBean userBOPageBean= UserMapper.MAPPER.user2UserBO(userPageBean);
System.out.println(userBOPageBean);
}
@Test
public void fun2() {
List list = new ArrayList();
list.add(DataUtil.build(User.class));
list.add(DataUtil.build(User.class));
list.add(DataUtil.build(User.class));
PageBean userPageBean = new PageBean();
userPageBean.setPages(10);
userPageBean.setPageNum(5);
userPageBean.setPageSize(8);
userPageBean.setResult(list);
userPageBean.setTotal(86L);
userPageBean.setUrl("getUserByCondition/");
PageBean userBOPageBean = new PageBean();
UserMapper.MAPPER.user2UserBO(userPageBean,userBOPageBean);
System.out.println(userBOPageBean);
}
}
## 进阶一:属性名不同 ### 待转换的类
目标类
转换接口
测试类