提问人:lotario 提问时间:10/25/2023 最后编辑:lotario 更新时间:10/26/2023 访问量:64
使用 JOIN FETCH 查询具有一对多 BI 关联的 DTO 投影时出现 NonUniqueResultException
NonUniqueResultException when querying with JOIN FETCH a DTO projection that has onetomany bi association
问:
我正在使用Spring Data JPA,我得到了以下实体:
订单实体:
public class Order {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToOne(mappedBy = "order",
cascade = CascadeType.ALL)
private Cart cart;
购物车实体:
public class Cart {
@Id
private Long id;
@OneToOne(fetch = FetchType.LAZY)
@MapsId
private Order order;
@OneToMany(mappedBy = "cart",
cascade = CascadeType.ALL,
orphanRemoval = true)
private List<OrderItem> orderItems;
OrderItem 实体:
public class OrderItem {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "cart_id")
private Cart cart;
我正在使用以下查询:
return em.createQuery("""
select new OrderDTO(
order.id,
order.cart
)
from Order order join fetch order.cart.orderItems
where order.id = :orderId
""", OrderDTO.class).setParameter("orderId", orderId).getSingleResult();
但是我得到:
jakarta.persistence.NonUniqueResultException: query did not return a unique result: 2
这 2 个是指具有相同cart_id的订单项目,而不是订单(没有重复的订单 ID)。但是,我不确定 Hibernate 为什么要这样做,因为:
Order Table:
+----+
| id |
+----+
| 1 |
+----+
Cart Table:
+----------+
| order_id |
+----------+
| 1 |
+----------+
// Since Cart has @MapsId.
OrderItem table:
+----+---------+
| id | cart_id |
+----+---------+
| 1 | 1 |
| 2 | 1 |
+----+---------+
我希望 hibernate 加入 FETCH 所有订单商品,而不仅仅是唯一的结果,因为它们是属于同一购物车的不同订单商品。
我不认为关联配置不正确,也许不是最佳配置,而是我使用的 SQL 查询缺少一些东西,但我不知道是什么。
我正在尝试使用 JOIN FETCH,因为 Cart 中的 OneToMany OrderItem 集合是默认的 LAZY,以便在同一提取中同时接收订单和购物车及其 orderItems。(以前我使用的是 EAGER 但那一团糟,它有 N+1 查询问题)。
如果有人能帮忙,我将不胜感激!
谢谢!
编辑有用的相关信息:
相反,如果我直接使用此查询返回订单实体
return em.createQuery("select order from Order order " +
"join fetch order.cart as cart join fetch cart.orderItems " +
"where order.id = :orderId", Order.class)
.setParameter("orderId", orderId)
.getSingleResult();
它有效,没有问题。但是,如果我像这样对 OrderDTO 应用相同的 SQL 查询:
return em.createQuery("""
select new OrderDTO(
order.id,
order.cart
)
from Order order join fetch order.cart as cart join fetch cart.orderItems
where order.id = :orderId
""", OrderDTO.class).setParameter("orderId", orderId).getSingleResult();
我得到
org.hibernate.query.SemanticException: query specified join fetching, but the owner of the fetched association was not present in the select list [SqmSingularJoin(PizzaApp.api.entity.order.Order(order).cart(cart) : cart)
订单DTO:
public record OrderDTO(
Long id,
Cart cart
) {
}
编辑2:
我还尝试过使用 POJO 作为 DTO 而不是记录,但仍然由于购物车丢失而得到。SemanticException
编辑 3:删除 fetch 指令(因为在查询 DTO 时不需要它)只留下 join 指令后,我又回到了
jakarta.persistence.NonUniqueResultException: query did not return a unique result
无论我使用什么
return em.createQuery("""
select new OrderDTO(
order.id,
order.cart
)
from Order order join order.cart as cart join cart.orderItems
where order.id = :orderId
""", OrderDTO.class).setParameter("orderId", orderId).getSingleResult();
或
return em.createQuery("""
select new OrderDTO(
order.id,
order.cart
)
from Order order join order.cart.orderItems
where order.id = :orderId
""", OrderDTO.class).setParameter("orderId", orderId).getSingleResult();
答:
如果在数据库上运行休眠生成的查询,则结果中将有 2 行。当您选择 (Order) 时,它起作用的原因是 Hibernate 将 2 行映射到 1 个 Order 对象中,其中包含 2 个基于实体映射类的 OrderItems。Hibernate 从不接触你的数据库,也不关心你的 DTO,他们没有 .Hibernate 根据你用 s 标记 s 的方式做出所有决定,比如 , 。
最简单的解决方案是使用 Entity(Ordering) 保留您的选择,并有一个映射器来映射到 OrderingDTO。Entity
@Entity
Classes
@Annotation
@Entity
@ManyToOne
评论
NonUniqueResultException
评论