Я работаю над онлайн-приложением Spring Boot 2.0 / Java 8
.Я использую Hibernate
в качестве ORM
рамки.У меня есть две сущности, Order
и OrderDetail
, показанные ниже:
@Entity
@Table(name = "orders")
public class Order extends AbstractEntityUuid {
@Column(name = "order_number", unique = true)
private String orderNumber;
@JsonBackReference
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
private User user;
@Column(name = "total_amount")
private BigDecimal totalAmount = BigDecimal.ZERO;
@CreatedDate
@Column(name = "created_on", columnDefinition = "DATETIME", updatable = false)
protected LocalDateTime created;
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
@JsonManagedReference
private Set<OrderDetail> items = new HashSet<>();
@OneToOne(fetch = FetchType.LAZY, orphanRemoval = true)
@JoinColumn(nullable = false, name = "card_details_id")
private CardDetails card;
@OneToOne(fetch = FetchType.LAZY, orphanRemoval = true)
@JoinColumn(nullable = false, name = "shipping_address_id")
private Address shippingAddress;
@OneToOne(fetch = FetchType.LAZY, orphanRemoval = true)
@JoinColumn(name = "billing_address_id")
private Address billingAddress;
//getters and setters
}
@Entity
@Table(name = "order_detail")
public class OrderDetail extends AbstractPersistable<Long> {
@Column(name = "quantity")
private Integer quantity;
@Column(name = "total_amount")
private BigDecimal totalAmount = BigDecimal.ZERO;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "order_id", nullable = false)
@JsonBackReference
private Order order;
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "product_id", nullable = false)
private Product product;
//getters and setters
}
Когда пользователь переходит к своим заказам, он должен иметь возможность видеть информацию, связанную только с самим заказом (без подробностей),По этой причине я получаю данные только из таблицы заказов.Ниже приводится мой репозиторий:
public interface OrderRepository extends CrudRepository<Order, Long> {
@Query("FROM Order o WHERE o.user.email = ?1")
List<Order> findOrdersByUser(String email);
}
В сервисе я просто вызываю вышеуказанный метод и преобразую его в аналог dto.
@Transactional(readOnly = true)
public List<OrdersPreviewDTO> getOrdersPreview(String email) {
List<Order> orders = orderRepository.findOrdersByUser(email);
return orderConverter.convertToOrderPreviewDTOs(orders);
}
В преобразователе используется Джексон ObjectMapper
объект под капотом.
List<OrdersPreviewDTO> convertToOrderPreviewDTOs(List<Order> orders) {
return orders.stream()
.map(o -> objectMapper.convertValue(o, OrdersPreviewDTO.class))
.collect(toList());
}
objectMapper
вводится Spring
и определяется в классе конфигурации:
@Bean
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.findAndRegisterModules();
return objectMapper;
}
Объект OrdersPreviewDTO
dto содержит толькоподмножество сущности Order, потому что, как я уже упоминал, на странице Orders я хочу показывать только высокоуровневые свойства заказов пользователя и ничего не относиться к их деталям.
@JsonIgnoreProperties(ignoreUnknown = true)
public class OrdersPreviewDTO {
private String orderNumber;
@JsonFormat(pattern = "dd/MM/yyyy HH:mm")
private LocalDateTime created;
private BigDecimal totalAmount;
@JsonCreator
public OrdersPreviewDTO(
@JsonProperty("orderNumber") String orderNumber,
@JsonProperty("created") LocalDateTime created,
@JsonProperty("totalAmount") BigDecimal totalAmount) {
this.orderNumber = orderNumber;
this.created = created;
this.totalAmount = totalAmount;
}
//getters and setters
}
Все работает нормально,сущность порядка автоматически преобразуется Джексоном в его аналог dto.Проблема выявляется при рассмотрении запроса, выполняемого Hibernate под капотом.В Hibernate разверните коллекцию сведений о заказе для каждого заказа и выполните запрос для извлечения данных из каждой дочерней коллекции:
select carddetail0_.id as id1_2_0_, carddetail0_.brand as brand2_2_0_, carddetail0_.created as created3_2_0_, carddetail0_.exp_month as exp_mont4_2_0_, carddetail0_.exp_year as exp_year5_2_0_, carddetail0_.last4 as last6_2_0_ from card_details carddetail0_ where carddetail0_.id=?
select address0_.id as id1_1_0_, address0_.created as created2_1_0_, address0_.last_modified as last_mod3_1_0_, address0_.city as city4_1_0_, address0_.country as country5_1_0_, address0_.first_name as first_na6_1_0_, address0_.last_name as last_nam7_1_0_, address0_.postal_code as postal_c8_1_0_, address0_.state as state9_1_0_, address0_.street_address as street_10_1_0_, address0_.telephone as telepho11_1_0_ from address address0_ where address0_.id=?
select items0_.order_id as order_id4_4_0_, items0_.id as id1_4_0_, items0_.id as id1_4_1_, items0_.order_id as order_id4_4_1_, items0_.product_id as product_5_4_1_, items0_.quantity as quantity2_4_1_, items0_.total_amount as total_am3_4_1_ from order_detail items0_ where items0_.order_id=?
и других тонн.
Буду ли я изменять код вследующим образом Hibernate запускает только ожидаемый запрос к таблице заказов:
Эта строка кода:
objectMapper.convertValue(o, OrdersPreviewDTO.class)
заменяется следующим грязным исправлением:
new OrdersPreviewDTO(o.getOrderNumber(), o.getCreated(), o.getTotalAmount())
Запрос от Hibernate:
select order0_.id as id1_5_, order0_.billing_address_id as billing_6_5_, order0_.card_details_id as card_det7_5_, order0_.created_on as created_2_5_, order0_.one_address as one_addr3_5_, order0_.order_number as order_nu4_5_, order0_.shipping_address_id as shipping8_5_, order0_.total_amount as total_am5_5_, order0_.user_id as user_id9_5_
from orders order0_ cross join user user1_
where order0_.user_id=user1_.id and user1_.user_email=?
Мой вопрос.Есть ли способ сказать Джексону, чтобы он отображал только поле Dtos, чтобы он не вызывал отложенную загрузку через Hibernate для необязательных полей?
Спасибо