У меня есть объект User, который имеет отношение к другим объектам (Order, Profile). Когда я собираю объект из dto и обратно, мне также нужно собрать объект order и profile.
Сущность пользователя:
@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();
}
}
Например, если мне нужно преобразовать объект User в 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 ответ
Взаимоотношения сущностей
Сначала вы можете нарисовать диаграмму ER для визуализации отношений сущностей. Диаграмма классов UML для Order
мог бы сделать вот так:
,----------------.
|Order |
|----------------|
|customer: User |
|orderedAt : Date|
|items: Car[] |
`----------------'
Связи:
- заказ имеет только одного клиента типа
User
(заказывается) - заказ подан в конкретную дату типа
Date
(заказывается в) - в заказе может быть ноль, один или несколько элементов типа
Car
(есть позиции заказа)
Дизайн класса
Действительный заказ должен как минимум соответствовать двум является-отношения.
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);
}
}
Это показывает, что вы можете просто создать ордер, передав User
объект. Другие атрибуты (например, адреса) могут быть получены от клиента или пользователя (делегирование).
Сопоставление: от DTO к Entity и наоборот
Для этого отображение есть много каркасы нравиться MapStruct, Бульдозер и т.п.
Вы также можете реализовать Шаблон адаптера себя, что здесь очень хорошо подходит.
Hibernate: это уже OR-mapper!
Спящий режим часто описывается как объектно-реляционный преобразователь (ИЛИ-картограф).
Потому что он отображает объектно-ориентированные структуры (выраженные в классах Java) на реляционные структуры данных (выраженные в таблицах базы данных) и обратно.
Тот факт, что вы используете JPA-аннотации @Entity
а также @Table
на классе описывает именно это отображение.
При правильном использовании Спящий режим а также JPA (и их аннотации), то вам не нужны дополнительные фреймворки сопоставления или сервисы сборки.