Является ли использование dao при сборке сущности в dto хорошим способом использования?

#java #hibernate #entity #dto #converters

Вопрос:

У меня есть пользовательская сущность,которая имеет отношение к другим сущностям(Заказ, профиль), Когда я собираю сущность из dto и обратно, мне тоже нужно собрать сущность заказа и профиля.

Пользовательская сущность:

 @Table(name = "user_info")
@Entity
@Getter
@Setter
public class User implements Serializable {
    private static final long serialVersionUID = 1789771068899105277L;
    @Id
    @GeneratedValue
    @Column(name = "userId",columnDefinition = "BINARY(16)")
    private UUID userId ;
    @Column(name = "login", unique = true, length = 20)
    private String login;
    @Column(name = "userPassword", length = 100)
    private String password;
    @Column(name = "userEmail", unique = true, length = 320)
    private String userEmail;


    @OneToOne(mappedBy = "user", cascade = CascadeType.ALL,
            fetch = FetchType.LAZY, optional = false)
    private Profile profile;

    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
    private Set<Role> roles = new HashSet<>();

    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Order> orders = new ArrayList<>();

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return Objects.equals(getUserId(), user.getUserId());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getUserId());
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("User{");
        sb.append("userId=").append(userId);
        sb.append(", login='").append(login).append(''');
        sb.append(", password='").append(password).append(''');
        sb.append(", userEmail='").append(userEmail).append(''');
        sb.append('}');
        return sb.toString();
    }
}




    @Override
    public int hashCode() {
        return Objects.hash(getId(), getUserAuthority());
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("Role{");
        sb.append("id=").append(id);
        sb.append(", userAuthority='").append(userAuthority).append(''');
        sb.append(", user=").append(user);
        sb.append('}');
        return sb.toString();
    }
}
 

Например, если мне нужно преобразовать сущность пользователя в Dto, мне нужен ассемблер:

 public interface Assembler <E,D>{
    D mergeAggregateIntoDto(E entity);
    E mergeDtoIntoAggregate(D dto);
}
@Service
public class UserAssembler implements Assembler<User, UserDto>{
    @Autowired
    private CarDao carDao;

    @Override
    public UserDto mergeAggregateIntoDto(User entity) {
      UserDto userDto = new UserDto();
      userDto.setUserId(entity.getUserId());
      userDto.setUserEmail(entity.getUserEmail());
      userDto.setLogin(entity.getLogin());
      userDto.setPassword(entity.getPassword());
      userDto.setProfile(mergeAggregateIntoDto(entity.getProfile()));
      userDto.setRoles(entity.getRoles().stream().map(this::mergeAggregateIntoDto).collect(Collectors.toSet()));
      userDto.setOrders(entity.getOrders().stream().map(this::mergeAggregateIntoDto).collect(Collectors.toList()));

      return userDto;
    }

    @Override
    public User mergeDtoIntoAggregate(UserDto dto) {
        User user = new User();
        Map<UUID,Car> uuidCarMap = getCars(dto);
        Profile profile = mergeDtoIntoAggregate(dto.getProfile());
        profile.setUser(user);
        user.setUserId(dto.getUserId());
        user.setUserEmail(dto.getUserEmail());
        user.setLogin(dto.getLogin());
        user.setPassword(dto.getPassword());
        user.setProfile(profile);
        user.setRoles(dto.getRoles().stream().map(roleDto->{
            Role role = mergeDtoIntoAggregate(roleDto);
            role.setUser(user);
            return role;
        }).collect(Collectors.toSet()));
        user.setOrders(dto.getOrders().stream().map(orderDto -> {
            Order order = mergeDtoIntoAggregate(orderDto);
            order.setUser(user);
            order.setCar(uuidCarMap.get(orderDto.getCarId()));
            return order;
        }).collect(Collectors.toList()));
        return user;
    }

    private Map<UUID, Car> getCars(UserDto userDto){
        List<UUID> carIds = userDto.getOrders().stream().map(OrderDto::getCarId).collect(Collectors.toList());
        List<Car> cars = carDao.findByIds(carIds);
        return cars.stream().collect(Collectors.toMap(Car::getCarId, Function.identity()));
    }

    private ProfileDto mergeAggregateIntoDto(Profile profile){
        ProfileDto profileDto = new ProfileDto();
        profileDto.setProfileId(profile.getProfileId());
        profileDto.setUserId(profile.getUser().getUserId());
        profileDto.setAccountBalance(profile.getAccountBalance());
        profileDto.setUserFirstName(profile.getUserFirstName());
        profileDto.setUserSurName(profile.getUserSurName());
        profileDto.setUserRegistrationDate(profile.getUserRegistrationDate());
        return profileDto;
    }
    private Profile mergeDtoIntoAggregate(ProfileDto profileDto){
        Profile profile = new Profile();
        profile.setProfileId(profileDto.getProfileId());
        profile.setAccountBalance(profileDto.getAccountBalance());
        profile.setUserFirstName(profileDto.getUserFirstName());
        profile.setUserSurName(profileDto.getUserSurName());
        profile.setUserRegistrationDate(profileDto.getUserRegistrationDate());
        return profile;
    }

    private RoleDto mergeAggregateIntoDto(Role role){
        RoleDto roleDto = new RoleDto();
        roleDto.setId(role.getId());
        roleDto.setUserAuthority(role.getUserAuthority());
        roleDto.setUserId(role.getUser().getUserId());
        return roleDto;
    }
    private Role mergeDtoIntoAggregate(RoleDto roleDto){
        Role role = new Role();
        role.setUserAuthority(roleDto.getUserAuthority());
        role.setId(roleDto.getId());
        return role;
    }
    private OrderDto mergeAggregateIntoDto(Order order){
        OrderDto orderDto = new OrderDto();
        orderDto.setOrderCost(order.getOrderCost());
        orderDto.setOrderDate(order.getOrderDate());
        orderDto.setOrderId(order.getOrderId());
        orderDto.setUserAddress(order.getUserAddress());
        orderDto.setUserDestination(order.getUserDestination());
        orderDto.setUserId(order.getUser().getUserId());
        orderDto.setCarId(order.getCar().getCarId());
        return orderDto;
    }
    private Order mergeDtoIntoAggregate(OrderDto orderDto){
        Order order = new Order();
        order.setOrderId(orderDto.getOrderId());
        order.setOrderCost(orderDto.getOrderCost());
        order.setUserAddress(orderDto.getUserAddress());
        order.setUserDestination(orderDto.getUserDestination());
        order.setOrderDate(orderDto.getOrderDate());
        return order;
    }

}
 

Итак, я использовал dao в ассемблере, и я хочу знать, могу ли я это сделать? Спасибо.

Комментарии:

1. У меня нет проблем с вашим нынешним подходом.

Ответ №1:

На мой взгляд, все в порядке, но я бы рекомендовал вам изучить представления сущностей с сохраняемостью Blaze, поскольку я думаю, что это идеальный вариант для его использования.

Я создал библиотеку, позволяющую легко сопоставлять модели JPA с пользовательским интерфейсом или абстрактными моделями, определенными классами, что-то вроде весенних прогнозов данных на стероидах. Идея заключается в том, что вы определяете свою целевую структуру(модель домена) так, как вам нравится, и сопоставляете атрибуты(получатели) с помощью выражений JPQL с моделью сущности.

Модель DTO для вашего варианта использования может выглядеть следующим образом с представлениями сущностей Blaze-Persistence:

 @EntityView(User.class)
@UpdatableEntityView
public interface UserDto {
    @IdMapping
    Long getUserId();
    String getUserEmail();
    void getUserEmail(String userEmail);
    String getLogin();
    void setLogin(String login);
    String getPassword();
    void setPassword(String password);
    ProfileDto getProfile();
    void setProfile(ProfileDto profile);

    @EntityView(Profile.class)
    @UpdatableEntityView
    interface ProfileDto {
        @IdMapping
        Long getProfileId();
        BigDecimal getAccountBalance();
        void setAccountBalance(BigDecimal accountBalance);
        String getUserFirstName();
        void setUserFirstName(String userFirstName);
        String getUserSurName();
        void setUserSurName(String userSurName);
        Instant getUserRegistrationDate();
        void setUserRegistrationDate(Instant userRegistrationDate);
    }
}
 

Запрос-это вопрос применения представления сущности к запросу, самым простым из которых является просто запрос по идентификатору.

UserDto a = entityViewManager.find(entityManager, UserDto.class, id);

Интеграция данных Spring позволяет использовать ее почти как проекции данных Spring: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features

 Page<UserDto> findAll(Pageable pageable);
 

Самое приятное то, что он будет получать только то состояние, которое действительно необходимо!

Сохранение/сброс — это вопрос вызова

entityViewManager.save(entityManager, userDto);

Поэтому вам не нужен стандартный код для преобразования одного представления в другое. У этой модели есть еще много преимуществ, таких как преимущества в производительности, но вы можете ознакомиться с подробностями в документации, если хотите: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#updatable-entity-views