5

Suppose I have some classes, like this:

OpA extends Operation
OpA1 extends OpA
OpA2 extends OpA
OpB extends Operation
OpB1 extends OpB
OpB2 extends OpB
OpB3 extends OpB
OpC extends Operation

                        Operation
                            |
            /-----------------------------\
           /                |              \
         OpA               OpB             OpC
         /                  |
        /                   |
   /------\           /-----------\
  /        \         /      |      \
 OpA1     OpA2     OpB1    OpB2    OpB3

If I want to find some operations, I can do this:

session.createCriteria(Operation.class)
       .add(...)
       .add(...)
       .addOrder(...)
       .setFirstResult(...)
       .setMaxResults(...)
       .list();

But what if I want to apply these criteria not to all operations of type Operation.class, but only to those of types: OpA2 + OpB1 + OpB3 + OpC?

My Question: How can I do it using Hibernate Criteria only? No HQL, please.

Note: I don't know if I should view this problem as "querying more than one class" (where all of them have fields with the same names as the ones I am querying), or if I view it as "restricting a query by a list of subclasses".

Edit: If I could create an interface TheOnesIWant, and then make classes OpA2, OpB1, OpB3 and OpC implement this interface, this would yield the result I want: session.createCriteria(TheOnesIWant.class) But I can't do this, because I only know the classes I want to query at runtime. The above class hierarchy is just an example.

Marcelo Glasberg
  • 29,013
  • 23
  • 109
  • 133
  • Can you write a simple sql depicting what you want considering these classes as table. – VGaur Jan 07 '15 at 04:42
  • Sorry, I don't know how. – Marcelo Glasberg Jan 07 '15 at 04:50
  • Could you create an interface, "Criteriable", which only those classes you want to apply the criteria to implement? Then session.createCriteria(Criteriable.class)? I've never tried this, but its the first thing that springs to mind :) – ConMan Jan 07 '15 at 09:48
  • I can't, because the classes I need change all the time, at runtime. – Marcelo Glasberg Jan 07 '15 at 18:07
  • You want to create this criteria in only 1 command? Considering that all the classes implements Operation, you could create one criteria for every class, and put all lists together for return. – Dalton Jan 09 '15 at 17:38
  • I can't create one criteria for every class and then add the results, because I need **paging**. For example, I want to retrieve 350 results after the 15000th one. – Marcelo Glasberg Jan 09 '15 at 18:02

1 Answers1

7

In general, Hibernate has very limited support for joining unrelated entities. By "unrelated" I mean entities that don't have references to each other. Critera API doesn't support it at all, and HSQL can do only cross join. Don't get me wrong, the underlying Criteria/HQL implementation is capable of joining unrelated entities, but the API do not expose this functionality. (You can do all kinds of joins (inner, left, right, full) on related entities.) The only way to do inner/left/right/full join on unrelated entities, is native sql. However, if the unrelated entities have common parent, you can use a hibernate feature called "implicit polymorphism": selecting the parent, will return parent and all its sub-classes. And then you can restrict the result to list of sub-classes.
So try this:

List<Operation> list = session.createCriteria(Operation.class)
    .add(Restrictions.or(
         Property.forName("class").eq(OpA2.class)
        ,Property.forName("class").eq(OpB1.class)
        ,Property.forName("class").eq(OpB3.class)
        ,Property.forName("class").eq(OpC.class)))
    .list();

EDIT:
Here is a example how to query properties that do not exist in the parent class: In this example properties "f" and "value" don't exist in the Operation class, they only exist in OpA2 abd OpC classes.

List<Operation> list = session.createCriteria(Operation.class)
    .add(Restrictions.or(
        Restrictions.and(Property.forName("class").eq(OpA2.class), Restrictions.eq("f", 1))
        , Property.forName("class").eq(OpB1.class)
        , Property.forName("class").eq(OpB3.class)
        , Restrictions.and(Property.forName("class").eq(OpC.class), Restrictions.eq("value", "b"))))
    .list();
outdev
  • 5,249
  • 3
  • 21
  • 38
  • OK, nice, all the classes I have are indeed related by the common parent `Operation.class`. But then it only seems to work if I am querying for some field that actually exists in the `Operation` class itself. If I query for some field that exists in all subclasses (`OpA2`, `OpB1`, `OpB3` and `OpC`) but NOT in `Operation`, then I get this exception: `MySQLSyntaxErrorException: Not unique table/alias`. – Marcelo Glasberg Jan 14 '15 at 03:20
  • See the EDIT section. – outdev Jan 14 '15 at 09:40