不能 select 容器

Cannot select container

我想用 HQL 创建新对象,所以我尝试发出以下查询:

    String hql = "SELECT new com.pizzaboy.dto.OrderDTO(o.adress, o.orderDate, dt, r, d)"
            + " FROM Order o JOIN o.user u "
            + " JOIN FETCH o.deliveryType dt"
            + " JOIN FETCH o.restaurant r "
            + " JOIN FETCH o.dishes d"
            //+ " JOIN FETCH d.dishType disht "
            + " WHERE u.id=:id";


它给了我以下错误,就好像 "o.dishes" 不是 List 或 Set:

org.hibernate.hql.internal.ast.QuerySyntaxException: Unable to locate appropriate constructor on class [com.pizzaboy.dto.OrderDTO]. Expected arguments are: com.pizzaboy.pojo.Adress, java.util.Date, com.pizzaboy.pojo.DeliveryType, com.pizzaboy.pojo.Restaurant, com.pizzaboy.pojo.Dish [SELECT new com.pizzaboy.dto.OrderDTO(o.adress, o.orderDate, dt, r, elements(d)) FROM com.pizzaboy.pojo.Order o JOIN o.user u  JOIN FETCH o.deliveryType dt JOIN FETCH o.restaurant r  JOIN FETCH o.dishes d WHERE u.id=:id]

不过,是Set,确实:

@Entity
@Table(name = "orders", catalog = "PIZZABOY")
public class Order implements java.io.Serializable {

private Integer id;
private Adress adress;
private DeliveryType deliveryType;
private Payment payment;
private Restaurant restaurant;
private User user;
private Date orderDate;
private Set<Dish> dishes = new HashSet<Dish>(0);

...
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
public Set<Dish> getDishes() {
    return this.dishes;
}

public void setDishes(Set<Dish> dishes) {
    this.dishes = dishes;
}

为什么会出现此错误以及如何解决?

PS OderDTO 构造函数

public OrderDTO(Adress adress, Date orderDate, DeliveryType deliveryType, Restaurant restaurant,
            List<Dish> dishes) {

您不能 return 以这种方式收集:当您执行 HQL/JPQL 时,您处于对象和关系世界之间的跨域中,结果是面向对象的,但是计算发生在面向 table 的 space.

为什么不让它变得简单?

String hql = "SELECT new com.pizzaboy.dto.OrderDTO(o)"
    + " FROM Order o JOIN o.user u "
    + " JOIN FETCH o.deliveryType dt"
    + " JOIN FETCH o.restaurant r "
    + " JOIN FETCH o.dishes d"
    + " WHERE u.id=:id";

public OrderDTO(Order order) 
{
    this.adress = order.getAddress();
    this.deliveryType = order.getDeliveryType();
    this.restaurant = order.getRestaurant();
    this.dishes = new ArrayList<>(order.getDishes());
}

Hiberante 将负责作为 ORM 的工作,加载相关的 entities/collections 实体并将它们放在正确的位置(因为您指定了 FETCH,这将可能 只发生一次 SQL 查询)。

似乎无法使用内联 FETCH 急切加载集合,原因是存在技术限制(也许它应该被认为是一个错误,类似于您无法加入的事实fecth 多个 bags 在单个查询中)。

所以你有两个选择:

  1. 从查询中删除 FETCH 并手动初始化惰性属性:

    SELECT new com.pizzaboy.dto.OrderDTO(o)
    FROM Order o 
        JOIN o.user u
    WHERE u.id=:id
    

    public OrderDTO(Order order) 
    {
        Hibernate.initialize(order.getAddress());
        this.adress = order.getAddress();
    
        Hibernate.initialize(order.getDeliveryType());
        this.deliveryType = order.getDeliveryType();
    
        Hibernate.initialize(order.getRestaurant());
        this.restaurant = order.getRestaurant();
    
        Hibernate.initialize(order.getDishes());
        this.dishes = new ArrayList<>(order.getDishes());
    }
    

    这会让您遇到 N+1 问题:可以使用 batch-fetch

  2. 避免
  3. 使用实体图/获取配置文件:

    SELECT new com.pizzaboy.dto.OrderDTO(o)
    FROM Order o 
        JOIN o.user u
    WHERE u.id=:id
    
    EntityGraph<Order> graph = entityManager.createEntityGraph(Order.class);
    graph.addAttributeNodes(Order_.deliveryType, Order_.restaurant);
    graph.addSubgraph(Order_.dishes);
    
    entityManager.createQuery(hql)
        .setHint("javax.persistence.fetchgraph", graph)
        .setParameter("id", userId)
        .getResultList();
    

    因为我从来没有使用自定义对象选择的实体图,这可能会遇到与使用内联连接提取相同的错误

但是,我认为 returning DTO 与相关的 Entity 几乎相同 没有多大用处: 只是 return 实体本身(可能你希望它处于 DETACHED 状态)。
显然,当您必须处理由不同实体组成的事物时,这并不适用 and/or 实体属性 and/or 函数计算,如计数、求和、a + b、...