rie*_*pil 20 java junit spring design-patterns spring-boot
我已经阅读了几篇文章和Stackoverflow帖子,用于将域对象转换为DTO,并在我的代码中尝试了它们.在测试和可扩展性方面,我总是面临一些问题.我知道以下三种可能的域对象转换为DTO的解决方案.大部分时间我都在使用Spring.
解决方案1:服务层中用于转换的私有方法
第一种可能的解决方案是在服务层代码中创建一个小的"帮助器"方法,该方法将检索到的数据库对象转换为我的DTO对象.
@Service
public MyEntityService {
public SomeDto getEntityById(Long id){
SomeEntity dbResult = someDao.findById(id);
SomeDto dtoResult = convert(dbResult);
// ... more logic happens
return dtoResult;
}
public SomeDto convert(SomeEntity entity){
//... Object creation and using getter/setter for converting
}
}
Run Code Online (Sandbox Code Playgroud)
优点:
缺点:
new SomeEntity()在私有方法中使用,如果对象是深层嵌套的,我必须提供足够的结果,when(someDao.findById(id)).thenReturn(alsoDeeplyNestedObject)以避免NullPointers如果convertion也解散了嵌套结构解决方案2:DTO中用于将域实体转换为DTO的附加构造函数
我的第二个解决方案是在我的DTO实体中添加一个额外的构造函数来转换构造函数中的对象.
public class SomeDto {
// ... some attributes
public SomeDto(SomeEntity entity) {
this.attribute = entity.getAttribute();
// ... nesting convertion & convertion of lists and arrays
}
}
Run Code Online (Sandbox Code Playgroud)
优点:
缺点:
new SomeDto()在服务代码,并为此我必须提供正确的嵌套对象结构作为我的结果someDao嘲讽.解决方案3:使用Spring的Converter或任何其他外部化Bean进行此转换
如果最近看到Spring提供了一个转换原因的类:Converter<S, T>但是这个解决方案代表了正在进行转换的每个外化类.有了这个解决方案,我将转换器注入到我的服务代码中,当我想将域实体转换为我的DTO时,我会调用它.
优点:
缺点:
你有更多解决我的问题的方法,你如何处理它?您是否为每个新的域对象创建一个新的Converter并且可以"生活"项目中的类数量?
提前致谢!
Ren*_*ink 15
解决方案1:服务层中用于转换的私有方法
我猜解决方案1不会运行良好,因为您的DTO是面向域的,而不是面向服务的.因此,很可能它们被用于不同的服务中.因此映射方法不属于一个服务,因此不应在一个服务中实现.您将如何在另一个服务中重用映射方法?
如果您按服务方法使用专用DTO,则1.解决方案将很有效.但最后还有更多关于此的内容.
解决方案2:DTO中用于将域实体转换为DTO的附加构造函数
通常是一个很好的选择,因为您可以将DTO视为实体的适配器.换句话说:DTO是实体的另一种表示.这样的设计通常包装源对象并提供方法,为您提供包装对象的另一个视图.
但是DTO是一个数据传输对象,所以它可能迟早被序列化并通过网络发送,例如使用spring的远程处理功能.在这种情况下,接收此DTO的客户端必须反序列化它,因此需要其类路径中的实体类,即使它只使用DTO的接口.
解决方案3:使用Spring的Converter或任何其他外部化Bean进行此转换
解决方案3是我也更喜欢的解决方案.但我会创建一个Mapper<S,T>负责从源到目标映射的接口,反之亦然.例如
public interface Mapper<S,T> {
public T map(S source);
public S map(T target);
}
Run Code Online (Sandbox Code Playgroud)
可以使用像modelmapper这样的映射框架来完成实现.
你还说每个实体都有一个转换器
随着我的域模型的增长,不会"缩放"那么多.有很多实体,我必须为每个新实体创建两个转换器( - >转换DTO权利和授权给DTO)
我建议您只需为一个DTO创建2个转换器或一个映射器,因为您的DTO是面向域的.
一旦您开始在另一个服务中使用它,您将认识到其他服务通常应该或不能返回第一个服务所做的所有值.您将开始为每个其他服务实现另一个映射器或转换器.
如果我从专用或共享DTO的优点和缺点开始,这个答案将会很长,所以我只能请你阅读我的博客服务层设计的优缺点.
Mak*_*iuk 11
我喜欢接受的答案中的第三个解决方案.
解决方案3:使用Spring的Converter或任何其他外部化Bean进行此转换
我DtoConverter用这种方式创建:
BaseEntity类标记:
public abstract class BaseEntity implements Serializable {
}
Run Code Online (Sandbox Code Playgroud)
AbstractDto类标记:
public class AbstractDto {
}
Run Code Online (Sandbox Code Playgroud)
GenericConverter接口:
public interface GenericConverter<D extends AbstractDto, E extends BaseEntity> {
E createFrom(D dto);
D createFrom(E entity);
E updateEntity(E entity, D dto);
default List<D> createFromEntities(final Collection<E> entities) {
return entities.stream()
.map(this::createFrom)
.collect(Collectors.toList());
}
default List<E> createFromDtos(final Collection<D> dtos) {
return dtos.stream()
.map(this::createFrom)
.collect(Collectors.toList());
}
}
Run Code Online (Sandbox Code Playgroud)
CommentConverter界面:
public interface CommentConverter extends GenericConverter<CommentDto, CommentEntity> {
}
Run Code Online (Sandbox Code Playgroud)
CommentConveter类实现:
@Component
public class CommentConverterImpl implements CommentConverter {
@Override
public CommentEntity createFrom(CommentDto dto) {
CommentEntity entity = new CommentEntity();
updateEntity(entity, dto);
return entity;
}
@Override
public CommentDto createFrom(CommentEntity entity) {
CommentDto dto = new CommentDto();
if (entity != null) {
dto.setAuthor(entity.getAuthor());
dto.setCommentId(entity.getCommentId());
dto.setCommentData(entity.getCommentData());
dto.setCommentDate(entity.getCommentDate());
dto.setNew(entity.getNew());
}
return dto;
}
@Override
public CommentEntity updateEntity(CommentEntity entity, CommentDto dto) {
if (entity != null && dto != null) {
entity.setCommentData(dto.getCommentData());
entity.setAuthor(dto.getAuthor());
}
return entity;
}
}
Run Code Online (Sandbox Code Playgroud)
我认为第三个解决方案是最好的。是的,您必须为每个实体创建两个新的转换类,但是当您需要进行测试时,您将不会有很多麻烦。您永远不应该选择解决方案,因为这将导致您一开始只编写较少的代码,然后在测试和维护该代码时编写更多的代码。
我最终没有使用任何神奇的映射库或外部转换器类,而只是添加了一个自己的小bean convert从每个实体到我需要的每个DTO的方法。原因是映射是:
要么非常简单,要么我只是将一些值从一个字段复制到另一个字段,也许使用小的实用程序方法,
还是非常复杂,与仅编写该代码相比,将自定义参数写到某个通用映射库会更加复杂。例如,在客户端可以发送JSON但在后台将其转换为实体的情况下,当客户端再次检索这些实体的父对象时,它将转换回JSON。
这意味着我可以调用.map(converter::convert)任何实体集合来获取我的DTO流。
在一堂课中拥有全部功能是否可扩展?那么,即使使用通用映射器,此映射的自定义配置也必须存储在某个位置。除少数情况外,代码通常非常简单,因此我不必担心此类的复杂性爆炸。我也不希望有更多的实体,但是如果我这样做了,我可以将这些转换器归类到每个子域的类中。
在我的实体和DTO的基础上添加一个基类,这样我就不需要(还是?)就可以编写一个通用的转换器接口并为每个类实现它。
| 归档时间: |
|
| 查看次数: |
12603 次 |
| 最近记录: |