Mak*_*iuk 7 java entity design-patterns dto spring-boot
主要问题是如何在不违反SOLID原则的情况下将DTO转换为实体以及将实体转换为Dto。
例如,我们有这样的json:
{ id: 1,
name: "user",
role: "manager"
}
Run Code Online (Sandbox Code Playgroud)
DTO是:
public class UserDto {
private Long id;
private String name;
private String roleName;
}
Run Code Online (Sandbox Code Playgroud)
实体是:
public class UserEntity {
private Long id;
private String name;
private Role role
}
public class RoleEntity {
private Long id;
private String roleName;
}
Run Code Online (Sandbox Code Playgroud)
并且有有用的Java 8 DTO转换模式。
但是在他们的示例中,没有一对多关系。为了创建UserEntity,我需要使用dao层(服务层)按RoleName获取Role。我可以将UserRepository(或UserService)注入对流器中吗?因为转换器组件似乎会破坏SRP,所以它只能转换,不能知道服务或存储库。
转换器示例:
@Component
public class UserConverter implements Converter<UserEntity, UserDto> {
@Autowired
private RoleRepository roleRepository;
@Override
public UserEntity createFrom(final UserDto dto) {
UserEntity userEntity = new UserEntity();
Role role = roleRepository.findByRoleName(dto.getRoleName());
userEntity.setName(dto.getName());
userEntity.setRole(role);
return userEntity;
}
....
Run Code Online (Sandbox Code Playgroud)
在对流器类中使用存储库好吗?还是应该创建另一个服务/组件来负责从DTO(例如UserFactory)创建实体?
尽量将转换与其他层解耦:
public class UserConverter implements Converter<UserEntity, UserDto> {
private final Function<String, RoleEntity> roleResolver;
@Override
public UserEntity createFrom(final UserDto dto) {
UserEntity userEntity = new UserEntity();
Role role = roleResolver.apply(dto.getRoleName());
userEntity.setName(dto.getName());
userEntity.setRole(role);
return userEntity;
}
}
@Configuration
class MyConverterConfiguration {
@Bean
public Converter<UserEntity, UserDto> userEntityConverter(
@Autowired RoleRepository roleRepository
) {
return new UserConverter(roleRepository::findByRoleName)
}
}
Run Code Online (Sandbox Code Playgroud)
您甚至可以定义自定义,Converter<RoleEntity, String>
但这可能会将整个抽象扩展得有点过头。
正如其他一些人指出的那样,这种抽象隐藏了应用程序的一部分,该部分在用于集合时可能表现不佳(因为 DB 查询通常可以批处理。我建议您定义一个Converter<List<UserEntity>, List<UserDto>>
在转换单个对象,但您现在可以批量处理您的数据库请求,而不是一个一个查询——用户不能错误地使用所述转换器(假设没有恶意)。
如果您希望在定义转换器时更加舒适,请查看MapStruct或ModelMapper。最后但并非最不重要的一点是给datus一个机会(免责声明:我是作者),它可以让您以流畅的方式定义您的映射,而无需任何隐式功能:
@Configuration
class MyConverterConfiguration {
@Bean
public Mapper<UserDto, UserEntity> userDtoCnoverter(@Autowired RoleRepository roleRepository) {
Mapper<UserDto, UserEntity> mapper = Datus.forTypes(UserDto.class, UserEntity.class)
.mutable(UserEntity::new)
.from(UserDto::getName).into(UserEntity::setName)
.from(UserDto::getRole).map(roleRepository::findByRoleName).into(UserEntity::setRole)
.build();
return mapper;
}
}
Run Code Online (Sandbox Code Playgroud)
(这个例子在转换一个 Collection<UserDto>
我认为这将是最可靠的方法,但给定的上下文/场景受到无法提取的依赖关系和性能影响,这让我认为强制 SOLID 在这里可能是一个坏主意。这是一个权衡
如果您有服务层,那么使用它来进行转换或将任务委托给转换器会更有意义。
理想情况下,转换器应该只是转换器:映射器对象,而不是服务。
现在,如果逻辑不太复杂并且转换器不可重用,您可以将服务处理与映射处理混合在一起,在这种情况下,您可以将前缀替换Converter
为Service
.
而且,如果只有服务与存储库进行通信,那就更好了。
Otherwise layers become blur and the design messy : we don't know really any longer who invokes who.
我会这样做:
controller -> service -> converter
-> repository
Run Code Online (Sandbox Code Playgroud)
或自行执行转换的服务(转换不太复杂且不可重用):
controller -> service -> repository
Run Code Online (Sandbox Code Playgroud)
现在说实话,我讨厌 DTO,因为这些只是数据重复。
我仅介绍它们,因为客户在信息方面的要求与实体表示不同,并且拥有自定义类(在本例中不是重复的)会变得更加清晰。
归档时间: |
|
查看次数: |
5795 次 |
最近记录: |