views:

1407

answers:

3

Suppose I have classes like:

class A {
 B getB();
 C getC();
}

class B {
 String getFoo();
}

class C {
 int getBar();
}

and I want to filter criteria on A, two filters on different subclass properties, like:

Criteria criteriaA = session.createCriteria(A.class);
Criteria criteriaB = criteriaA.createCriteria("b").add(Restrictions.eq("foo", "Something"));
Criteria criteriaC = criteriaA.createCriteria("c").add(Restrictions.eq("bar", 0));

What I want to do is combine criteriaB and criteriaC using an "or" clause, something like:

//this does not work
criteriaA.add(Restrictions.disjunction().add(criteriaB).add(criteriaC));

How can I accomplish this? I am stumbling a little over the API here.

+3  A: 

Use aliases instead of nested criteria:

Criteria criteria = session.createCriteria(A.class)
 .createAlias("b", "b_alias")
 .createAlias("c", "c_alias")
 .add(Restrictions.disjunction()
  .add(Restrictions.eq("b_alias.foo", "Something"))
  .add(Restrictions.eq("c_alias.bar", "0"))
 );
ChssPly76
The aliases are unnecessary.
mR_fr0g
Trying this out - thanks again, Chess
RMorrisey
How would you do this without the aliases?
RMorrisey
see my answer i've posted
mR_fr0g
The aliases **may** be unnecessary in this specific case. They are necessary if you're going to use a deeper path or if any pair of your conditions uses the same base path (e.g. `b.foo` and `b.bar`)
ChssPly76
This works for one case; but in the other case, I am doing a Restrictions.sqlRestriction, and it doesn't use the alias correctly. Any idea how to work with it?
RMorrisey
I should have said, this is a simplified example, my actual use case has a few nested levels of properties for each filter. Sorry if this is was a source of problems
RMorrisey
Is `sqlRestriction` based on nested property? It's been a while since I've used it so I'm not 100% sure on this - but I think Hibernate will preserve your aliases as long as you specify them for each path fragment (e.g. for A.B.C.D you'd do createAlias() for `A.B`, then `B_alias.C` and `C_alias.D`). You should then be able to use ssqlRestriction on `C_alias.D` (ignore the '{alias}' bit in the docs). Does that not work for you? Are aliases being regenerated?
ChssPly76
I found that the alias for the 2nd table is not being joined to the resulting query, even when I eliminate the SQL restriction and create a readonly property to do Restrictions.eq() on. It seems that adding a restriction based on an alias doesn't force a join of the aliased path. Adding this seems to fix the problem:criteria.createCriteria("b").setFetchMode("b2", FetchMode.JOIN)
RMorrisey
Worked for me too. Thanks.
afsharm
+2  A: 

You only need to create one criteria object like so.

Criteria criteria = session.createCriteria(A.class);
criteria.add(Restriction.disjunction()
    .add(Restriction.eq("b.foo", "something"))
    .add(Restriction.eq("c.bar", 0)));
mR_fr0g
This doesn't work; I get an error like: org.hibernate.QueryException: could not resolve property: b.foo of: com.myproject.A
RMorrisey
It sounds like this would work with only one level of properties, but not for the actual filter I am using.
RMorrisey
+1  A: 

In case someone else finds it useful, I found a more complicated answer to the problem which appears to be allowed by the API, though I did not get to test it before ChssPly posted his (simpler) solution:

DetachedCriteria bValues = DetachedCriteria.forClass(A.class);
bValues.createCriteria("b").add(Restrictions.eq("foo", "something"));

DetachedCriteria cValues = DetachedCriteria.forClass(A.class);
cValues.createCriteria("c").add(Restrictions.eq("bar", 0));

Restrictions.or(Subqueries.in("id", bValues), Subqueries.in("id", cValues));
RMorrisey
Interesting, I've never thought to try it this way. The resulting query would be quite different (and, possibly, slower) from the one obtained via aliases, though. But this may be the way to solve your sqlRestriction issue if aliases fail.
ChssPly76