无法选择容器

时间:2016-09-13 23:31:12

标签: java hibernate hql jpql

我想用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) {

1 个答案:

答案 0 :(得分:1)

您无法以这种方式返回集合:当您执行HQL / JPQL时,您处于对象与关系世界之间的跨领域,其中结果是面向对象的,但计算发生在面向表的空间上

为什么不简单?

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的形式开展工作,加载相关实体/实体集合并将它们放在正确的位置(因为您指定了FETCH,这可能可能只用一个SQL查询发生。)

似乎不可能使用内联FETCH急切地加载集合,原因是存在技术限制(可能它应该被视为一个错误,类似于您无法加入fecth倍数的事实单个查询中的bags

所以你剩下2个选择:

  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. 使用实体图/获取配置文件:

    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();
    

    由于我从未使用具有自定义对象选择的实体图,因此可能会遇到与使用内联联接提取相同的错误

  3. 但是,我认为返回DTO与相关Entity 几乎相同DETACHED没什么用处:只返回实体本身(可能是你想要的)它在 e.g.: DateTime.ParseExact("12/02/21 10:56:09", "yy/MM/dd HH:mm:ss", CultureInfo.InvariantCulture ).ToString("MMM. dd, yyyy HH:mm:ss") 州。) 显然,当你必须处理由不同实体和/或实体属性和/或函数计算(如count,sum,a + b,...等)组成的东西时,这不适用