2
\$\begingroup\$

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.

\$\endgroup\$
1
  • \$\begingroup\$ This question actually reads like it should belong to stackoverflow rather than codereview. \$\endgroup\$ Sep 5, 2021 at 1:19

1 Answer 1

1
\$\begingroup\$

Entity Relationships

First you could draw the ER diagram to visualize entity-relations. An UML class-diagram for Order could do, like this:

,----------------.
|Order           |
|----------------|
|customer: User  |
|orderedAt : Date|
|items: Car[]    |
`----------------'

Relations:

  • an order has exactly a single customer of type User (is ordered by)
  • an order is submitted at a specific date of type Date (is ordered at)
  • an order can have zero, one or many items of type Car (has order line items)

Class Design

A valid order must at least fulfill the two is-relationships.

class Order {
    final Date orderedAt;
    final User customer;
    final List<Car> lineItems = new ArrayList<Car>();

    public Order(User customer, Date orderedAt) {
        this.orderedAt = orderedAt;
        this.customer = customer;
    }

    public Order(User customer) {
        this(customer, new Date());  // default: ordered today
    }

    public Address getInvoiceAddress() {
        return this.customer.getUserAddress();  // delegate to customer (user)
    }

    public Address getDeliveryAddress() {
        return this.customer.getDestinationAddress();  // delegate to customer (user)
    }

    public void addItem(Car item) {
        this.lineItems.add(item);
    }
}

This shows, that you can simply construct an order by passing a User object. The other attributes (e.g. addresses) can be derived from the customer or user (delegation).

Mapping: from DTO to Entity and vice-versa

For this mapping there are plenty of mapping frameworks like MapStruct, Dozer etc.

You can also implement the Adapter Pattern yourself, which fits pretty well here.

Hibernate: is an OR-mapper already!

Hiberernate is often described as object-relational mapper (OR-mapper).

Because it maps from object-oriented structures (expressed in Java classes) to relational data structures (expressed in database tables) and back.

The fact, that you are using the JPA-annotations @Entity and @Table on the class describes exactly this mapping.

When properly using Hibernate and JPA (and their annotations), then you don't need further mapping frameworks or assembly-services.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.