Search code examples
javahibernatecriteria

Is it allowed to use the same DetachedCriteria within one Criteria multiple times?


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.


Solution

  • 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 JPA javax.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