简体   繁体   中英

JPA @OneToMany Fetch Overrides CriteriaQuery Join Condition

I am quite new to Hibernate and the Criteria API, and am running into troubles using them.

There are two Entities:

@Entity
public class Product {
    @Id
    @GeneratedValue
    private Integer id;

    private String productName;

    @OneToMany(fetch = FetchType.LAZY,
            cascade = CascadeType.ALL)
    private List<ProductPrice> prices = new ArrayList<>();
}

@Entity
public class ProductPrice {
    @Id
    @GeneratedValue
    private Integer id;

    private BigDecimal price;

    private String region;

    private LocalDate startDate;
}

Products have multiple ProductPrices. Each ProductPrice belongs to a Region. The goal is to query Products and all their historical Prices for a specific Region:

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Product> cq = cb.createQuery(Product.class);
Root<Product> root = cq.from(Product.class);

ListJoin<Product, ProductPrice> productJoin = root.join(Product_.prices, JoinType.INNER);
productJoin.on(cb.equal(productJoin.get(ProductPrice_.region), "REGION1"));
List<Product> products = em.createQuery(cq.distinct(true)).getResultList();

This generates the following SQL Query:


   select
        distinct product0_.id as id1_1_,
        product0_.productName as productN2_1_ 
    from
        Product product0_ 
    inner join
        (
            Product_ProductPrice prices1_ 
        inner join
            ProductPrice productpri2_ 
                on prices1_.prices_id=productpri2_.id
            ) 
                on product0_.id=prices1_.Product_id 
                and (
                    productpri2_.region=?
                )

I tried that query and it seems to work, however as soon as getPrices() is called on one of the Products, the Product's Prices are lazily fetched, like so:

    select
        prices0_.Product_id as Product_1_2_0_,
        prices0_.prices_id as prices_i2_2_0_,
        productpri1_.id as id1_3_1_,
        productpri1_.price as price2_3_1_,
        productpri1_.region as region3_3_1_ 
    from
        Product_ProductPrice prices0_ 
    inner join
        ProductPrice productpri1_ 
            on prices0_.prices_id=productpri1_.id 
    where
        prices0_.Product_id=?

which makes sense, because of the association @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL) , but in this case, for this specific query, I don't want this behaviour. I did not find an example like that in the Hibernate UserGuide or here on stackoverflow so I guess I am missing something very obvious. But still, I couldn't find a solution to my problem.

Thanks!

As mentioned in the comments above, on specifies the columns needed for the join. In your situation, you need a where .

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Product> cq = cb.createQuery(Product.class);
Root<Product> root = cq.from(Product.class);

ListJoin<Product, ProductPrice> productJoin = root.join(Product_.prices, JoinType.INNER);
cq.where(cb.equal(productJoin.get(ProductPrice_.region), "REGION1"));
List<Product> products = em.createQuery(cq).getResultList();

In addition, you should have a look whether your @OneToMany mapping is designed efficiently like this. This excellent article of Vlad Mihalcea describes how to map a @OneToMany efficiently: Either make it bidirectional or unidirectional with @ManyToOne .

For the issue of your lazy loads, have a look for lazy initialisation. I really like graphs for doing this.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM