Hibernate generates multiple JOINs in query with WHERE clause on entities from single table inheritance

23 Views Asked by At

In our application we use hibernate single table inheritance strategy to differentiate between comparison types during a query.

As our software is not open source, I will try to abstract our implementation as much as possible.

Our entity model in this case persists of two tables, one of which is a ParentEntity which has multiple ChildEntities.

The java implementation of these entities looks something like this:

@Entity
@Table(name = TableEntity.TBL_NAME)
public class TableEntity {
    …
}


@Entity
@Table(name = ParentEntity.TBL_NAME)
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "PARENT_ATTR")
public abstract class ParentEntity {
    …
}


@Entity
@DiscriminatorValue(“CHILD_ENTITY_1)
public class ChildEntity1 extends ParentEntity {
    …
}


@Entity
@DiscriminatorValue("CHILD_ENTITY_2”)
public class ChildEntity2 extends ParentEntity {
    …
}

The TableEntity is what we are trying to query for.

The ParentEntity is the joined comparison type and marked as SingleTableInheritance.

The ChildEntity extends the ParentEntity for one additional field.

We use single table inheritance strategy for performance benefits and because we don't want the amount of tables on our DB to overflow.

Because we use a relatively complex filtering mechanism, we combine a multitude of predicates into one larger criteria query.

The method calls for the predicate look something like this:

Join<TableEntity, ParentEntity> join = tableEntityRoot.join(TableEntity.joinAttribute);

predicates.add(cb.equal(cb.treat(join, ChildEntity1.class).get(ChildEntity1.CHILD_ATTR_1), comparisonValue));

predicates.add(cb.equal(cb.treat(join, ChildEntity2.class).get(ChildEntity2.CHILD_ATTR_2), comparisonValue));

The TableEntity and ParentEntity are joined together.

The where clause predicates are built upon the fields introduced by the ChildEntities.

The "treat" operator is necessary for mapping the ChildEntities to the ParentEntity. Otherwise, hibernate will not know that the join has the ChildEntities fields.

When executing this query, I would except hibernate to make a single join from TableEntity to ParentEntity, as we are using hibernates single table inheritance strategy.

The resulting query looks something like this though:

select * from TABLE t
    inner join PARENT_TABLE pt1
            on t.PT_ID = pt1.PT_ID
    inner join PARENT_TABLE pt2
            on t.PT_ID = pt2.PT_ID
    inner join PARENT_TABLE pt3
            on t.PT_ID = pt3.PT_ID
    inner join PARENT_TABLE pt4
            on t.PT_ID = pt4.PT_ID
    …
where (pt1.PARENT_ATTR in (…))
and (p2.CHILD_ATTR_1 = comparisonValue
    or p3.CHILD_ATTR_2 = comparisonValue
    or p4.CHILD_ATTR_3 = comparisonValue
);

The PARENT_TABLE is joined once for every first level inheritance it has. There are multiple joins for the SAME table.

This behavior causes problems when executing the where clause, as the clauses themselves might be correct, but the combination isn't. Particularly the combination between "where in" and the "equal" clauses have contradicting results.

In particular, the "treat" operator seems to cause the problem, as without it, only a single join is done. If you leave out the "treat" operator though, hibernate runs into an exception because it can't find the ChildEntity fields.

I've been doing quite some research on this online, but couldn't find anything and am a little at a loss right now. Help would be appreciated.

0

There are 0 best solutions below