JPA @OneToMany Fetch переопределяет критерии условия запроса на присоединение - PullRequest
0 голосов
/ 01 февраля 2020

Я довольно новичок в Hibernate и Criteria API и сталкиваюсь с проблемами при их использовании.

Есть две сущности:

@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;
}

Продукты имеют несколько ProductPrices. Каждая ProductPrice принадлежит Региону. Цель состоит в том, чтобы запросить продукты и все их исторические цены для определенного c региона:

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

. Это генерирует следующий запрос SQL:


   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=?
                )

Я пробовал этот запрос и, похоже, это работает, однако, как только getPrices() вызывается для одного из Продуктов, Цены на Продукт лениво выбираются, например:

    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=?

, что имеет смысл из-за ассоциации @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL), но в этом случае для этого конкретного c запроса я не хочу такого поведения. Я не нашел подобного примера в Hibernate UserGuide или здесь, в stackoverflow, так что я предполагаю, что упускаю что-то очень очевидное. Но все же я не смог найти решение своей проблемы.

Спасибо!

1 Ответ

0 голосов
/ 02 февраля 2020

Как упоминалось в комментариях выше, on указывает столбцы, необходимые для объединения. В вашей ситуации вам понадобится 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();

. Кроме того, вы должны посмотреть, насколько эффективно ваше отображение @OneToMany выглядит следующим образом. В этой превосходной статье Влада Михалча описывается, как эффективно отобразить @OneToMany: либо сделайте его двунаправленным, либо однонаправленным с @ManyToOne.

Для решения проблем с ленивыми нагрузками посмотрите для ленивой инициализации. Мне очень нравятся графики для этого.

...