我决定了,业内标准定义的“欧”实在太多了,我只用几个就够了,后面重构项目也只用几个就行了。
但是!“欧”之间的转换好麻烦呀,先网罗几个业内常用的转换手段,最后哪个用起来最符合人体工学 —— 最舒服吧~
🍒 一个荔枝
数据库表:
id bigint not null comment '用户ID' primary key,
username varchar(255) default '' not null comment '用户名',
password varchar(255) default '' not null comment '密码',
salt varchar(255) default '' not null comment '随机盐值',
question varchar(255) default '' not null comment '密保问题',
answer varchar(255) default '' not null comment '密保答案',
create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间',
update_time datetime default CURRENT_TIMESTAMP not null comment '更新时间',
constraint uk_username unique (username) comment '用户名唯一索引'
PO(字段和数据库表一一对应,除了下划线和驼峰转换):
@Data
public class UserPO implements Serializable {
/**
* 用户id
*/
private Long id;
/**
* 用户名
*/
private String username;
/**
* 密码
*/
private String password;
/**
* 随机盐值
*/
private String salt;
/**
* 密保问题
*/
private String question;
/**
* 密保答案
*/
private String answer;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新时间
*/
private Date updateTime;
private static final long serialVersionUID = 1L;
}
假设有一个只返回前端 username 字段的 VO:
@Data
public class UserInfoVO implements Serializable {
private String username;
private static final long serialVersionUID = 1226886631190798234L;
}
DTO 是这样的:
@Data
public class UserLoginDTO implements Serializable {
@NotBlank(message = "用户名不能为空")
@Pattern(regexp = "^[a-zA-Z0-9_-]{4,16}$", message = "用户名格式错误")
private String username;
@NotBlank(message = "密码不能为空")
@Length(min = 6, max = 20, message = "密码长度必须在6到20之间")
private String password;
private static final long serialVersionUID = 1L;
}
在 DTO 这里我们可以使用一些校验框架来做字段校验。这个就是前端页面或者是服务调用方传给我们的参数。也就是账号和密码来完成登陆。
之后我们开始来进行转换吧。
🍉 使用 Beanutils 进行转换
这个是比较简单的一个方法:
import org.springframework.beans.BeanUtils;
public class SpringBeanUtilsExample {
public static void main(String[] args) {
// 创建一个 PO 对象
User po = new UserPO();
po.setUserId(1L);
po.setUsername("user");
po.setPassword("password");
po.setSalt("salt");
po.setQuestion("question");
po.setAnswer("answer");
po.setCreateTime(new Date());
po.setUpdateTime(new Date());
// PO 转换为 VO
UserInfoVO vo = new UserInfoVO();
BeanUtils.copyProperties(po, vo);
System.out.println("PO转换为VO:" + vo.getUsername());
// 创建一个 DTO 对象
UserLoginPO dto = new UserLoginPO();
dto.setUsername("user");
dto.setPassword("password");
// DTO 转换为 PO
UserPO poFromDTO = new UserPO();
BeanUtils.copyProperties(dto, poFromDTO);
System.out.println("DTO 转换为 PO:" + poFromDTO.getUsername());
}
}
这里需要注意的是,上面用的是:
import org.springframework.beans.BeanUtils;
BeanUtils.copyProperties(Object source, Object target)
还有一种用法:
import cn.hutool.core.bean.BeanUtil;
BeanUtil.copyProperties(Object source, Object target)
这两个 copyProperties 方法看起来很像,但它们分别来自不同的框架:
- 一个来源于 Spring 框架(Spring 体系下更常用)
- 一个是 Hutool 工具包(更偏向通用 Java 工具类)
它们在底层实现和使用细节上有一些区别:
- 空值处理
- Hutool 的
copyProperties默认不会忽略空值属性。 - Spring 的
copyProperties也不会忽略空值,但可以借助BeanWrapper或其他方式自定义过滤。
- Hutool 的
- 类型转换支持
- Hutool 会做一些基础的类型自动转换(如
String→Integer)。 - Spring 默认不支持自动类型转换,需要类型完全匹配。
- Hutool 会做一些基础的类型自动转换(如
- 嵌套对象复制
- Hutool 不支持复杂嵌套 Bean 的深度复制。
- Spring 同样不支持嵌套结构的深层复制,只是字段名称和类型一致才复制。
- 源和目标的属性映射规则
- 两者都要求属性名和类型匹配,才能完成复制。
- Spring 的版本更严格,如果属性不兼容,会直接抛出异常(如
NoSuchMethodException)。
如果只需要做快速的 Bean 属性复制,不考虑 Spring 依赖,可以用 Hutool,更轻量;如果在 Spring 项目里,可以直接用 Spring 的 BeanUtils,避免引入额外依赖。
其实两个都打差不大,因为大家都习惯于在 Spring 项目里安装 Hutool。
🍑 使用 MapStcurt 进行转换
其实除非不得已,我是不太想用这个方式的,毕竟又要引入依赖,又要一堆配置,配配配。
不过都说这个方式是大厂的标准转换方法,嗯,大厂嘛,那总归也要看看再说吧。
首先,在 pom.xml 文件中添加 MapStruct 的依赖:
xmlCopy code<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
</dependency>
然后,定义一个 Mapper 接口:
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
@Mapper
public interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
@Mapping(source = "username", target = "name")
UserInfoVO toUserInfoVO(UPanUser po);
@Mapping(source = "name", target = "username")
UserPO toUserPO(UserLoginPO dto);
}
下面看一个它在 Spring 中的应用:
TODO... 算了,定义转换类太麻烦了,以后再说吧 先收藏一个链接,下次一定:别再用 BeanUtils 了,这款 PO VO DTO 转换神器不香么?
🥝 使用 ModelMapper 进行转换
import org.modelmapper.ModelMapper;
public class ModelMapperExample {
public static void main(String[] args) {
// 创建一个PO对象
UserPO po = new UserPO();
po.setUserId(1L);
po.setUsername("user");
po.setPassword("password");
po.setSalt("salt");
po.setQuestion("question");
po.setAnswer("answer");
po.setCreateTime(new Date());
po.setUpdateTime(new Date());
// 创建 ModelMapper 对象
ModelMapper modelMapper = new ModelMapper();
// PO 转换为 VO
UserInfoVO vo = modelMapper.map(po, UserInfoVO.class);
System.out.println("PO 转换为 VO:" + vo.getUsername());
// 创建一个 DTO 对象
UserLoginPO dto = new UserLoginPO();
dto.setUsername("user");
dto.setPassword("password");
// DTO 转换为 PO
UserPO poFromDTO = modelMapper.map(dto, UserPO.class);
System.out.println("DTO转换为PO:" + poFromDTO.getUsername());
}
}
使用 ModelMapper 可以更加灵活地定义属性映射规则,只需创建一个 ModelMapper 对象,然后调用其 map 方法即可实现对象之间的属性拷贝。
🌹 鸣谢
感谢这个沸点评论区的大佬们,你们的回答帮助很大,撒花★,°:.☆( ̄▽ ̄)/$:.°★ 。
以上内容总结于网络文章 + 论坛坛友的实际工作。