About the use of @ForceDiscriminator/@DiscriminatorOptions(force=true)

10,963

Solution 1

As I'm running over this again and again, I think it might help to clarify: First, it is true that Hibernate does not require discrimination when using JOINED_TABLE mapping. However, it does require it when using SINGLE_TABLE. Even more importantly, other JPA providers mostly do require it.

What Hibernate actually does when performing a polymorphic JOINED_TABLE query is to create a discriminator named clazz on the fly, using a case-switch that checks for the presence of fields unique for concrete subclasses after outer-joining all tables involved in the inheritance-tree. You can clearly see this when including the "hibernate.show_sql" property in your persistence.xml. In my view this is probably the perfect solution for JOINED_TABLE queries, so the Hibernate folks are right to brag about it.

The matter is somewhat different when performing updates and deletes; here hibernate first queries your root-table for any keys that match the statement's where clause, and creates a virtual pkTable from the result. Then it performs a "DELETE FROM / UPDATE table WHERE pk IN pkTable" for any concrete class withing your inheritance tree; the IN operator causes an O(log(N)) subquery per table entry scanned, but it is likely in-memory, so it's not too bad from a performance perspective.

To answer your specific question, Hibernate simply doesn't see a problem here, and from a certain perspective they are correct. It would be incredibly easy for them to simply honour the @DiscriminatorValue annotations by injecting the discriminator values during entityManager.persist(), even if they do not actually use them. However, not honoring the discriminator column in JOINED_TABLE has the advantage (for Hibernate) to create a mild case of vendor lockin, and it is even defensible by pointing to superior technology.

@ForceDiscriminator or @DiscriminatorOptions(force=true) sure help to mitigate the pain a little, but you have to use them before the first entities are created, or be forced to manually add the missing discriminator values using SQL statements. If you dare to move away from Hibernate it at least costs you some code change to remove these Hibernate specific annotations, creating resistance against the migration. And that is obviously all that Hibernate cares about in this case.

In my experience, vendor lockin is the paradise every market leader's wildest dreams are about, because it is the machiavellian magic wand that protects market share without effort; it is therefore done whenever customers do not fight back and force a price upon the vendor that is higher than the benefits reaped. Who said that an Open Source world would be any different?

p.s, just to avoid any confusion: I am in no way affiliated to any JPA implementor.

p.p.s: What I usually do is ignore the problem until migration time; you can then formulate an SQL UPDATE ... FROM statement using the same case-switch-with-outer-joins trick Hibernate uses to fill in the missing discriminator values. It's actually quite easy once you have understood the basic principle.

Solution 2

Guys let me try to explain about @DiscriminatorOptions(Force=true). Well , it is used in single table inheritence, i have recently used this in one of the scenario. i have two entities which was mapped to single table. when i was trying to fetch the record for one entity i was getting list of result containg records from both the entities and this was my problem. To solve this problem i have used @DiscriminatorOptions(Force=true) which will create the predicate using Discriminator column with the specified value mapped to the corresponding entity. so the query will be look like this after i used @DiscriminatorOptions(Force=true)

select * 
from TABLE 
where  YOUR PREDICATE AND DiscriminatorColumn = DiscriminatorValue

Solution 3

I think this is more of my opinion but I think some will agree with me. I prefer the fact that Hibernate enables you to not use a discriminator. In several cases the discriminator isn't necessary.

For example, I have a Person entity which contains stuff like a name, a date of birth, etc. This entity can be used by several other entities like Employee or Customer. When I don't reference Person from other entities, but reference Employee or Customer instead, the discriminator isn't used as Hibernate is instructed to fetch either one.

Share:
10,963
yannisf
Author by

yannisf

Developer

Updated on June 07, 2022

Comments

  • yannisf
    yannisf about 2 years

    Why is @ForceDiscriminator or its equivalent @DiscriminatorOptions(force=true) necessary in some cases of inheritance and polymorphic associations? It seems to be the only way to get the job done. Are there any reasons not to use it?

  • yannisf
    yannisf almost 12 years
    Good point, but still there are cases where hibernate will just not work (WrongClassException). Why hibernate does not work out of the box without forcing?
  • siebz0r
    siebz0r almost 12 years
    @yannisf I believe that depends on your code/design. I've used Hibernate several times without discriminators and never had this problem.
  • yannisf
    yannisf almost 9 years
    Thank you for your comment. instanceOf with regards to dynamic proxies (see lazy loading) has proven to be a very unreliable tool. Additionally, instanceOf is a design antipattern in the OO world. Finally, one would have to code arbitrary logic to decide on the actual class resolution.
  • Christian Beikov
    Christian Beikov over 3 years
    If you think something is wrong in Hibernate you are welcome to participate by starting a discussion on e.g. the mailing list about this topic. Hibernate is an open source project that implements the JPA specification. if the JPA specification is not clear about this topic, then it's the specifications fault. Hibernate has a past as well and tries to balance backwards compatibility and specification compatibility. Since it's open source, you can change it yourself, so you're not locked in. If you don't want to maintain it yourself, pay someone to do it for you.