Hi there!
I’ve been endlessly searching for an answer to this seemingly simple question: Is it possible to limit the amount of joins to just 1+1 when using the JOINED strategy, or will Hibernate ALWAYS join all the sub-classes of a super-class in order to find the correct one? I’ve read about the @DiscriminatorColumn/Value annotations, but they don’t seem to do the trick. And what I’ve read often is contradictory.
We have a normalized database which has one generic parent table, and 16 specific ones which, in the code, extend that parent. Currently, the fact that Hibernate joins all 16 tables when querying the parent class is really taking its toll on our response time. I would much rather move everything towards a more generic model and use composition instead of inheritance. But for now, that is not an option.
I’m using Hibernate 6.2.7.Final.
Many thanks in advance.
What didn’t work with them? Hibernate supports a special kind of “mixed” inheritance, where you can use both InheritanceType.JOINED
so spread values to multiple subtables and a @DiscriminatorColumn
to prevent having to create join by just reading the value of this column on the root table. You can enable this by mapping your root class like:
@Entity
@Inheritance( strategy = InheritanceType.JOINED )
@DiscriminatorColumn( discriminatorType = DiscriminatorType.STRING )
public class ParentEntity {
...
}
That would be the easiest solution, requiring just 1 more column on the database schema side of things, in the long run you can evaluate using InheritanceType.SINGLE_TABLE
, associations or embeddable aggregates to create a more efficient mapping.
Awesome to hear that it must be possible 
This is my (simplified) setup:
@Entity(name = "Sample")
@Inheritance(strategy = JOINED)
@DiscriminatorColumn(name = “type”)
public abstract class ParentEntity extends UUIdentifiedEntity {
private Type type; //this is an enum
(..rest omitted..)
}
@Entity(name = “Child1Entity”)
@DiscriminatorValue(value = “Child1”)
public class ChildOneEntity extends ParentEntity {
(..rest omitted..)
}
@Entity(name = "Child2Entity")
@DiscriminatorValue(value = “Child2”)
public class ChildTwoEntity extends ParentEntity {
(..rest omitted..)
}
public enum Type {
Child1,
Child2,
etc.
}
Yet, when looking at the generated SQL, I still see all sub-classes being joined.
Maybe the fact that I’m using an enum is part of the problem? Or the abstract class?
Thanks for the help!
The @DiscriminatorColumn
you are using has to be managed by Hibernate, so I would not map it to an attribute of your entity especially unless it’s one of the supported types (enum isn’t) and its marked insertable = false, updatable = false
. You don’t need to handle it yourself, Hibernate will manage its values under the hood.
Well, if you need to read all the attributes of all the subtypes your root entity implements, of course every table will be joined because Hibernate will need to read every column across each one of them. It depends how you’re trying to load the entity in question.
Generally, to reduce the number of joins, you can ask for a specific subclass when using e.g. session.find()
, or filter the in queries with the treat()
or type()
operators.
I totally forgot to mention that indeed. One of the ways I’ve tried, is to create a ParentEntityRepository extends JpaRepository<ParentEntity, UUID>
With a custom method which has the type as one of its parameters:
List findAllByType(Type type)
It was my hope that, since Hibernate now has all the necessary ingredients to figure out what the correct subclass is, that this would happen automagically (with the help of the DiscriminatorColumn/Value annotations). Too bad enum isn’t supported. The fact that my enum is mapped to a String in the database doesn’t matter either?
You can indeed filter by type, but you don’t need to map the discriminator column to do that, the correct HQL syntax is:
... from ParentEntity p where type(p) = :type
Where the :type
parameter can be set to the subclass instance you want to filter:
.setParameter("type", ChildOneEntity.class);
You can simply remove the attribute mapping alltogether, the @DiscirminatorColumn
will be automatically handled by Hibernate and you can interact with types with the operators I mentioned.
I see, thanks for the clarification.
So just to conclude:
Querying by using ParentEntity.class + a distinguishable property (which is configured as a DiscriminatorColumn annotation in the parent and specified in a DiscriminatorValue in the child) and NOT join all other child entities is not possible.
Instead one should query using the specific subclasses in order to prevent joining all the other child classes.
It IS possible: you just annotated the root entity with @DiscriminatorColumn
, without adding any attribute mappings to the class itself. The column value will be handled by Hibernate, and used to filter subtypes according to the values configured with @DiscriminatorValue
.
Obviously, the joins will still be needed when reading the actual properties of the subtype tables.
Ok now I’m confused. Sorry about that… The help is really appreciated.
This is what my ParentEntity looks like:
@Entity(name = “Parent”)
@Inheritance(strategy = JOINED)
@DiscriminatorColumn(name = “type”)
public abstract class SampleEntity<T> extends UUIdentifiedEntity {
@Column(insertable = false, updatable = false)
private String type;
}
//And then one of the (many) children:
@Entity(name = “ChildOne”)
@DiscriminatorValue(value = “Child1)
public class ChildOneEntity extends ParentEntity {
//specific properties of child1
}
//This is how I query the database:
return session.createNamedQuery(“ParentEntity.byType”, ParentEntity.class)
.setParameter(“type”, type)
.getResultList();
//or
return parentRepository.findAllByType(type)
As far as I understand, the ‘type’ column is managed by Hibernate.
I’m not reading any properties of the other subtypes. I just want to get all the Children matching that type, and to help Hibernate out, I’ve added the DiscirminatorColumn/Value.
So what I expected to happen:
hibernate predetermining the correct subtype based on the type parameter I provided, in combination with the matching DiscriminatorColumn/Value, and then using it:
select * from Parent join where type = :type
But instead, Hibernate joins the other Child tables as well.
Remove this:
@Column(insertable = false, updatable = false)
private String type;