0

I have User entity that has relationship to other entities(Order,Profile) When I assemble entity from dto and back I need assemble order and profile entity too.

User entity:

@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();
    }
}

For example If I need transform User entity to Dto I need assembler:

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;
    }

}

So I used dao in assembler and I want to know can I do that? Thanks.

DozezQuest
  • 179
  • 7

1 Answers1

0

Looks ok to me but I would recommend you to look into Blaze-Persistence Entity Views as I think this is a perfect use case for it.

I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.

A DTO model for your use case could look like the following with Blaze-Persistence Entity-Views:

@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);
    }
}

Querying is a matter of applying the entity view to a query, the simplest being just a query by id.

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

The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features

Page<UserDto> findAll(Pageable pageable);

The best part is, it will only fetch the state that is actually necessary!

Saving/Flushing is a matter of calling

entityViewManager.save(entityManager, userDto);

So you don't need any boilerplate code for transforming from one representation into the other. There are many more benefits like performance advantages of this model, but you can read into the details in the documentation if you want: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#updatable-entity-views

Christian Beikov
  • 15,141
  • 2
  • 32
  • 58