I am just wondering if it is allowed in Hibernate to use the same DetachedCriteria object within one Criteria multiple times. Imagine the following case:
DetachedCriteria dCriteria = DetachedCriteria.forClass(A.class)
.add(Restrictions.eq("id", 1))
.setProjection(Projections.property("id"));
Criteria criteria = session.createCriteria(B.class)
.add(
Restrictions.or(
Restrictions.and(
Subqueries.exists(dCriteria),
Restrictions.eq("id", 1)
),
Restrictions.and(
Subqueries.notExists(dCriteria),
Restrictions.eq("id", 2)
)
)
.setProjection(Projections.property("id"));
Is the usage of dCriteria twice within the this criteria allowed? It seems to work but i am not sure if it might lead to problems in more complex cases (maybe the DetachedCriteria saves same state information during query generation?). I already did some reasearches but i couldn't find an explicit answer.
No it isn't (always) safe to re-use DetachedCriteria
. For example: get a list and a rowcount, reusing the DetachedCriteria
:
DetachedCriteria dc = DetachedCriteria.forClass(A.class);
Criteria c1 = dc.getExecutableCriteria(session);
c1.setProjection(Projections.rowCount());
long count = ((Number) c1.uniqueResult()).longValue();
System.out.println(count + " result(s) found:");
Criteria c2 = dc.getExecutableCriteria(session);
System.out.println(c2.list());
This prints:
Hibernate: select count(*) as y0_ from A this_
4 result(s) found:
Hibernate: select count(*) as y0_ from A this_ <-- whoops
[4] <-- whoops again
Really simple things that don't alter the DetachedCriteria
might be safe, but in general wrap the generation in some kind of factory and re-generate them each time you need them.
Officially, cloning DetachedCriteria
on each call to getExecutableCriteria
will never happen. See their issues, particularly HHH-635 and HHH-1046 where Brett Meyer states: "The Criteria API is considered deprecated", and the developers guide (v4.3 §12) which states:
Hibernate offers an older, legacy
org.hibernate.Criteria
API which should be considered deprecated. No feature development will target those APIs. Eventually, Hibernate-specific criteria features will be ported as extensions to the JPAjavax.persistence.criteria.CriteriaQuery
.
EDIT: In your example you re-use the same DetachedCriteria inside the same query. The same caveats therefore apply - if you, for instance, use setProjection
with one of the uses, things go wrong with the second use. For example:
DetachedCriteria dCriteria = DetachedCriteria.forClass(A.class)
.add(Restrictions.eq("id", 1))
.setProjection(Projections.property("id"));
Criteria criteria = session.createCriteria(B.class)
.add(
Restrictions.or(
Restrictions.and(
Subqueries.exists(dCriteria
.add(Restrictions.eq("text", "a1")) // <-- Note extra restriction
.setProjection(Projections.property("text"))), // <-- and projection
Restrictions.eq("idx", 1)
),
Restrictions.and(
Subqueries.notExists(dCriteria),
Restrictions.eq("idx", 2)
)
))
.setProjection(Projections.property("id"));
Object o = criteria.list();
This yields the SQL:
select this_.idx as y0_ from B this_
where (
(exists
(select this_.text as y0_ from A this_ where this_.id=? and this_.text=?) and this_.idx=?)
or (not exists
(select this_.text as y0_ from A this_ where this_.id=? and this_.text=?) and this_.idx=?))
We didn't ask for the text=?
part of the not exists
, but we got it due to the re-use of the DetachedCriteria
†
† This leads to bad situations where, if you applied the .add(Restrictions.eq("text" ...
to both uses of dCriteria
, it would appear twice in both the exists
and not exists
in the SQL