JPA2 Критерии запросов по иерархии объектов - PullRequest
5 голосов
/ 28 сентября 2010

предположим, у меня есть следующий домен объекта:

@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="TYPE")
public abstract class Entity1 {
//some attributes
}

@Entity 
@DiscriminatorValue("T1")
public class Entity2 extends Entity1 {
    @OneToMany(fetch=FetchType.EAGER, cascade = { CascadeType.ALL }, mappedBy="parent")
    @Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
    private Set<Entity1Detail> details = new HashSet<Entity1Detail>();
}

@Entity
public class Entity1Detail {
    @ManyToOne
    @JoinColumn(name="REF")
    private Entity2 parent;

    @Basic
    private Integer quantity;
}

@Entity
@DiscriminatorValue("T2")
public class Entity3 extends Entity1 {
//some other attributes
}

когда я делаю JPQL-запрос:

select e from Entity1 e left join e.details d where d.quantity > 1

работает хорошо (оставлено соединение; P). Однако, когда я пытаюсь построить тот же запрос, используя JPA2 Criteria API:

CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery q = builder.createQuery();
Root r = q.from(Entity1.class);
q.select(r);
q.where(builder.gt(r.join("details", JoinType.LEFT).get("quantity"), 1));

Я получаю NPE в "join", потому что атрибут "details" не принадлежит Entity1 (что на самом деле верно, мне вместо этого нужно выбрать Entity2.class) Дело в том, что когда мне нужно построить свой динамический запрос с использованием Criteria API, я действительно ничего не знаю об иерархии, я просто передаю класс.

Я понимаю, что Criteria API безопасен для типов и все такое, но есть ли способ обойти это? возможно, с псевдонимами (как и раньше, я использовал Hibernate Criteria API, обход соединения с псевдонимами):

Criteria c = session.createCriteria(Entity1.class);
c.createAlias("details", "d");
c.add(Restrictions.ge("d.quantity", 1));

1 Ответ

3 голосов
/ 28 февраля 2011

Вы должны основать свой запрос на entity2.details. Поскольку API критериев является типобезопасным, он обнаруживает, что entity1 не имеет поля с именем «details»

CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery q = builder.createQuery();
Root r = q.from(Entity2.class);    // Must use subclass as root
q.select(r);
q.where(builder.gt(r.join("details", JoinType.LEFT).get("quantity"), 1));

Поскольку Entity2 расширяет Entity1, вы можете безопасно преобразовать результаты в родительский тип. Например:

CriteriaQuery<Entity1> q = builder.createQuery(Entity1.class);
Root r = q.from(Entity2.class);    // Must use subclass as root

вернет список Entity1

CriteriaQuery<Entity2> q = builder.createQuery(Entity2.class);
Root r = q.from(Entity2.class);    // Must use subclass as root

вернет список Entity2

EDIT:

Я думаю, что неправильно понял цель здесь. Если вы хотите, чтобы все Entity1 НЕ БЫЛИ, если они Entity2 с details.quantity <= 1, вам нужно сделать больше. </p>

Вы не можете использовать левое соединение от Entity1Detail к Entity1, потому что это не является строго типобезопасным. Вместо этого вам нужно как-нибудь присоединить Entity2 к Entity1Detail. Вероятно, лучший инструмент для использования здесь - это коррелированный подзапрос .

CriteriaQuery<Entity1> q = builder.createQuery(Entity1.class);
Root<Entity1> ent1 = q.from(Entity1.class);

SubQuery<Entity2> subq = q.subquery(Entity2.class);
Root<Entity2> ent2 = subq.from(Entity2.class);
Path<Integer> quantity = ent2.join("details", JoinType.LEFT).get("quantity");
Predicate lessThan = builder.lte(quantity,1);
Predicate correlatedSubqJoin = cb.equal(ent1,ent2)
subq.where(lessThan, correlatedSubqJoin);

q.select(ent1);
q.where(builder.exists(subq).not());

API критериев не знает, что вы наследуете одну таблицу, поэтому вы должны написать свои запросы для всех стратегий наследования, включая стратегию наследования Joined.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...