【发布时间】:2015-04-26 12:35:53
【问题描述】:
我在我的 Web 应用程序中使用 Spring MVC 架构和 JPA。在哪里手动(即不使用任何框架)将数据传输对象 (DTO) 转换为 JPA 实体,反之亦然?
【问题讨论】:
标签: java spring-mvc jpa
我在我的 Web 应用程序中使用 Spring MVC 架构和 JPA。在哪里手动(即不使用任何框架)将数据传输对象 (DTO) 转换为 JPA 实体,反之亦然?
【问题讨论】:
标签: java spring-mvc jpa
我建议另一种没有额外依赖的方法:
import org.springframework.beans.BeanUtils
...
BeanUtils.copyProperties(sourceObject, targetObject);
可用于将 DTO 转换为实体,反之亦然,前提是它们具有相同的属性类型和名称。
如果您想忽略某些字段,只需在targetObject 之后添加它们即可。
BeanUtils.copyProperties(sourceObj, targetObj, "propertyToIgnoreA", "propertyToIgnoreB", "propertyToIgnoreC");
来源:http://appsdeveloperblog.com/dto-to-entity-and-entity-to-dto-conversion/
我认为这是最干净的方式。记得查看 Javadoc 中的警告!
【讨论】:
BeanUtils.copyProperties 是一个可行的解决方案,但它仍然基于反射 API,因此与基于构建时代码生成的 MapStruct 和 Orika 等工具相比,每次转换都会产生相当大的开销。 MapStruct 非常简单,我建议在性能很重要的情况下使用它而不是 BeanUtils
使用 mapstruct 库。另外在 build.gradle 中添加了以下内容
sourceSets {
main.java.srcDirs += "build/generated/sources/annotationProcessor/java/main/"
}
【讨论】:
我可以推荐使用mapstruct 库:
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
<version>1.2.0.Final</version>
</dependency>
例如,如果你有这样一个实体:
public class Entity {
private Integer status;
private String someString;
private Date startDate;
private Date endDate;
// SKIPPED
还有 DTO:
public class Dto {
private Boolean status;
private String someString;
private Long startDate;
private Long endDate;
// SKIPPED
那么就可以在服务层通过这种方式进行转换:
@Service
public class SomeServiceImpl implements SomeService {
@Autowired
SomeDao someDao;
@Autowired
SomeMapper someMapper;
public Dto getSomething(SomeRequest request) throws SomeException {
return someDao.getSomething(request.getSomeData())
.map(SomeMapper::mapEntityToDto)
.orElseThrow(() -> new SomeException("..."));
}
Mapper可以表示如下:
@Mapper
public interface SomeMapper {
@Mappings(
{@Mapping(target = "entity",
expression = "java(entity.getStatus() == 1 ? Boolean.TRUE : Boolean.FALSE)"),
@Mapping(target = "endDate", source = "endDate"),
@Mapping(target = "startDate", source = "startDate")
})
Dto mapEntityToDto(Entity entity);
}
【讨论】:
这是一个已接受答案的老问题,但尽管使用模型映射器 API 以简单的方式更新它。
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>0.7.4</version>
</dependency>
使用此 API,您可以避免手动设置器和获取器,如已接受答案中所述。
在我看来,两种转换都应该在控制器上进行,借助私有实用方法并使用 Java8 流的映射(如果交换了 DTO 集合),如 this article 中所示。
它应该发生在控制器上,因为 DTO 是独占传输对象。我不会进一步降低我的 DTO。
您在实体上编写服务和数据访问层,并在调用服务方法之前将 DTO 转换为实体,并在从控制器返回响应之前将实体转换为 DTO。
我更喜欢这种方法,因为实体很少更改,并且可以根据需要在 DTO 中添加/删除数据。
详细的模型映射器配置和规则描述here
【讨论】:
我想你是在问在哪里写整个实体-->DTO 转换逻辑。
喜欢你的实体
class StudentEntity {
int age ;
String name;
//getter
//setter
public StudentDTO _toConvertStudentDTO(){
StudentDTO dto = new StudentDTO();
//set dto values here from StudentEntity
return dto;
}
}
你的 DTO 应该是这样的
class StudentDTO {
int age ;
String name;
//getter
//setter
public StudentEntity _toConvertStudentEntity(){
StudentEntity entity = new StudentEntity();
//set entity values here from StudentDTO
return entity ;
}
}
你的控制器应该是这样的
@Controller
class MyController {
public String my(){
//Call the conversion method here like
StudentEntity entity = myDao.getStudent(1);
StudentDTO dto = entity._toConvertStudentDTO();
//As vice versa
}
}
【讨论】:
在我看来
它使您可以更好地控制流程,并且您不必在每次更改填充实体的某些逻辑时都更改服务/持久性类。
【讨论】: