Java中如何实现Dto与Entity互转?UserDto转UserEntity及表创建方案
一、先修正UserEntity配置,保证数据库表正确生成
首先要修正JPA注解的错误,确保实体类能被JPA正确解析并生成对应表结构:
import jakarta.persistence.*; // 或 javax.persistence.*,根据你的JPA版本选择 import java.util.List; @Entity @Table(name = "user") // 可指定表名,不指定默认以类名小写作为表名 public class UserEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) // 主键自增,根据数据库类型调整策略 private int id; // 注意:userId字段无明确业务意义,建议删除,否则会生成多余的数据库列 // private int userId; @Column(name = "parent_id") private Integer parentId; // 用Integer类型,支持存储null(根节点的父ID应为null) private String name; @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) @JoinColumn(name = "parent_id", referencedColumnName = "id") // 明确关联父表的主键字段 private List<UserEntity> users; // JPA必须的无参构造函数 public UserEntity() {} // Getter & Setter方法 public int getId() { return id; } public void setId(int id) { this.id = id; } public Integer getParentId() { return parentId; } public void setParentId(Integer parentId) { this.parentId = parentId; } public String getName() { return name; } public void setName(String name) { this.name = name; } public List<UserEntity> getUsers() { return users; } public void setUsers(List<UserEntity> users) { this.users = users; } }
关键配置说明:
@Id必须大写,否则JPA无法识别主键- 添加
@GeneratedValue指定主键生成策略,避免手动维护主键值 parentId用Integer类型,适配根节点无父ID的场景(int类型无法存储null)@OneToMany添加orphanRemoval = true,确保删除父节点时自动清理关联的子节点(可选,根据业务需求调整)- 必须提供无参构造函数,JPA实例化实体时依赖该方法
配置完成后,JPA会自动生成一张user表,结构如下:
| 字段名 | 类型 | 约束 |
|---|---|---|
| id | int | 主键、自增 |
| parent_id | int | 外键(关联id)、允许为空 |
| name | varchar | 允许为空 |
二、实现UserDto与UserEntity的相互映射
方式1:手动编写映射工具类
适合简单场景,无需引入第三方依赖:
import java.util.ArrayList; import java.util.List; public class UserMapper { // UserDto转UserEntity public static UserEntity dtoToEntity(UserDto dto) { if (dto == null) return null; UserEntity entity = new UserEntity(); entity.setId(dto.getId()); entity.setName(dto.getName()); // 递归转换嵌套的子节点列表,并设置子节点的parentId if (dto.getUsers() != null && !dto.getUsers().isEmpty()) { List<UserEntity> childEntities = new ArrayList<>(); for (UserDto childDto : dto.getUsers()) { UserEntity childEntity = dtoToEntity(childDto); childEntity.setParentId(entity.getId()); childEntities.add(childEntity); } entity.setUsers(childEntities); } return entity; } // UserEntity转UserDto public static UserDto entityToDto(UserEntity entity) { if (entity == null) return null; UserDto dto = new UserDto(); dto.setId(entity.getId()); dto.setName(entity.getName()); // 递归转换嵌套的子节点列表 if (entity.getUsers() != null && !entity.getUsers().isEmpty()) { List<UserDto> childDtos = new ArrayList<>(); for (UserEntity childEntity : entity.getUsers()) { childDtos.add(entityToDto(childEntity)); } dto.setUsers(childDtos); } return dto; } // 批量转换方法(可选) public static List<UserEntity> dtoListToEntityList(List<UserDto> dtoList) { if (dtoList == null) return null; List<UserEntity> entityList = new ArrayList<>(); for (UserDto dto : dtoList) { entityList.add(dtoToEntity(dto)); } return entityList; } public static List<UserDto> entityListToDtoList(List<UserEntity> entityList) { if (entityList == null) return null; List<UserDto> dtoList = new ArrayList<>(); for (UserEntity entity : entityList) { dtoList.add(entityToDto(entity)); } return dtoList; } }
使用示例:
// DTO转Entity UserDto userDto = new UserDto(); userDto.setId(1); userDto.setName("张三"); UserEntity userEntity = UserMapper.dtoToEntity(userDto); // Entity转DTO UserDto convertedDto = UserMapper.entityToDto(userEntity);
方式2:使用MapStruct自动生成映射代码
适合复杂场景,减少重复代码编写,需引入第三方依赖:
1. 添加Maven依赖
<dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct</artifactId> <version>1.5.5.Final</version> </dependency> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>1.5.5.Final</version> <scope>provided</scope> </dependency>
2. 定义Mapper接口
import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.factory.Mappers; import java.util.List; @Mapper public interface UserMapper { UserMapper INSTANCE = Mappers.getMapper(UserMapper.class); // UserDto转UserEntity,忽略parentId(需手动设置) @Mapping(target = "parentId", ignore = true) UserEntity dtoToEntity(UserDto dto); // 递归转换子节点列表 List<UserEntity> dtoListToEntityList(List<UserDto> dtoList); // UserEntity转UserDto UserDto entityToDto(UserEntity entity); // 递归转换子节点列表 List<UserDto> entityListToDtoList(List<UserEntity> entityList); }
3. 使用示例
// DTO转Entity UserDto userDto = new UserDto(); userDto.setId(1); userDto.setName("张三"); UserEntity userEntity = UserMapper.INSTANCE.dtoToEntity(userDto); // 手动设置子节点的parentId if (userEntity.getUsers() != null) { for (UserEntity child : userEntity.getUsers()) { child.setParentId(userEntity.getId()); } } // Entity转DTO UserDto convertedDto = UserMapper.INSTANCE.entityToDto(userEntity);
关键说明
- MapStruct会在编译时自动生成映射实现类,无需手动编写转换逻辑
- 子节点的
parentId需手动设置,因为DTO中没有该字段,无法自动映射
三、额外注意事项
- Spring Boot环境下,需在
application.properties中配置JPA自动建表:spring.jpa.hibernate.ddl-auto=update(开发环境),生产环境建议设置为none,手动管理表结构 - 递归映射时注意层级深度,避免栈溢出,层级过深建议改用迭代方式处理
- 保持DTO和Entity的字段类型兼容,减少类型转换错误
内容的提问来源于stack exchange,提问作者Taron
相关产品推荐
相关产品推荐

