Hibernate: Why should I Force Discriminator?

Hibernate is an ambitious project that aims to be a complete solution to the problem of managing persistent data in java. Even with such an arduous task before them, the hibernate team tries very hard to expose a simple API for developers like us. Still, the complexity behind the API shows its ugly face time and again and I believe it is unavoidable as long as the mismatch between Object and Relational world exists.

That said, although I have worked with hibernate for many years and have been its advocate in all my organizations, I keep facing newer issues and keep finding newer ways to work with it efficiently and effectively. Recently, when I was working for nboomi.com, I faced an issue when mapping a OneToMany relationship to the sub-classes of “Single Table Inheritance” strategy. After a frustrating couple of hours of debugging I finally landed on the correct solution. So, I thought other developers who will travel this path could get benefited and started writing this blog post.

Let me explain the issue I faced with an example. Assume you have a normal User Entity with the typical id, version and loginId properties. Assume this User can have many AboutUs sections and many Service sections. You don’t need to be an architect to model them as OneToMany relationships from User. So, I modelled UserAboutSection and UserServiceSection entities and created a OneToMany relationship between User and these entities. Looking at the commonality between these two, I decided to factor out the common fields into a superclass called UserSection. Now, both UserAboutSection and UserServiceSection extends UserSection. I chose to map this Inheritance hierarchy using “Single Table Inheritance” strategy to keep it simple and since most of the fields were common and only a few were specific.

The User entity is given below. Notice the List<UserAboutSection> and a List<UserServiceSection> mapped using OneToMany relationship.

The getters, setters, adders, imports and static imports are omitted for brevity.

@Entity @Table(name = "user")
public class User extends BaseEntity {

    @Column(name = "login_id")
    private String loginId;

    @Column(name = "password")
    private String password;

    ...

    @OneToMany(cascade = {CascadeType.ALL})
    @JoinColumn(name = "user_id", nullable = false)
    @IndexColumn(name = "user_section_position")
    List<UserAboutSection> aboutUs;

    @OneToMany(cascade = {CascadeType.ALL})
    @JoinColumn(name = "user_id", nullable = false)
    @IndexColumn(name = "user_section_position")
    List<UserServiceSection> services;

    ... Getters, Setters and Adders
}

Here goes the UserSection entity that acts as the base class in this “Single Table Inheritance” strategy. Hibernate uses the @DiscriminatorColumn annotation to distinguish between sub-classes.

@Entity @Table(name = "user_section")
@Inheritance(strategy= InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="user_section_type", discriminatorType = STRING) 
public class UserSection extends BaseEntity {
    
    @Column(name = "title")         
    protected String title;

    @Column(name = "description")   
    protected String description;

    ... Getters and Setters
}

Here goes the UserAboutSection entity that derives from the UserSection entity. Hibernate uses the @DiscriminatorValue annotation to decide if a row in the database belongs to an instance of this class.

@Entity @DiscriminatorValue("ABOUT")
public class UserAboutSection extends UserSection {
    @ManyToOne 
    @JoinColumn(name="user_id",updatable=false,insertable=false,nullable=false)
    protected User user;

    ... Other Properties specific to UserAboutSection
}

Here goes the UserServiceSection entity that derives from the UserSection entity. Hibernate uses the @DiscriminatorValue annotation to decide if a row in the database belongs to an instance of this class.

@Entity @DiscriminatorValue("SERVICE")
public class UserServiceSection extends UserSection {
    @ManyToOne 
    @JoinColumn(name="user_id",updatable=false,insertable=false,nullable=false)
    protected User user;

    ... Other Properties specific to UserServiceSection
}

Pretty straightforward… huh! When you try to retrieve an instance of User along with its aboutUs and services collections eagerly (or lazily – doesn’t matter), what do you expect?

I expected an instance of User with the aboutUs collection filled with only UserAboutSection instances and the services collection filled with only UserServiceSection instances corresponding to only the rows they represent in the database. And I believe this expectation is valid, because that is what the mapping looks like and hibernate also has all the information it needs to make this work.

But I got something different. Both the aboutUs and services collections had all the UserSection rows that belong to this User. I mean, aboutUs collection had all the UserSection instances including UserAboutSection and UserServiceSection instances. This was surprising because hibernate has all the information it needs to populate the right instances.

After quite a bit of debugging, googling and RTFM-ing I landed upon @ForceDiscriminator annotation. This annotation has to be applied to the base class in the Inheritance hierarchy for “Single Table Inheritance” strategy. In my case, I had to apply it to UserSection entity. The UserSection entity after applying this annotation is given below…

@Entity @Table(name = "user_section")
@Inheritance(strategy= InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="user_section_type", discriminatorType = STRING) 
@ForceDiscriminator
public class UserSection extends BaseEntity {
    
    @Column(name = "title")         
    protected String title;

    @Column(name = "description")   
    protected String description;

    ... Getters and Setters
}

Once I ask hibernate to Force Discriminiator, it is happy and populates the aboutUs and services collections with its respective instances.

Ok, Problem Solved! But why did I have to tell hibernate to Force Discriminator. Shouldn’t that be the default behaviour. Is it a bug in hibernate or is it a feature? Am I missing something? If any one of you hibernate fans have walked this path and know the answer, please feel free to drop in a comment. I sincerely hope this post will be a valuable time-saver for other hibernate developers who step on this Bug/Feature.

Ganeshji Marwaha

I spend my days as the Director of Technology for Mobility practice and help my clients design enterprise and consumer mobile strategies. Mobile Payments, Digital Wallet and Tokenization technologies are my areas of specialization

  • Doug

    I use Hibernate in production as well. My advice to you: do not use fancy/hype funtionalities such as inheritance/many-to-many relation. I just use Hibernate to abstract the DB layer. I even hesitate to apply entity relationship around my models.

    Doug

  • Mark

    Don’t listen to Doug. If you don’t do OO with Hibernate, then you are not abstracting the db layer, only the db type.

    Anyway, I can’t see all your code but it seems your problem is that you modeled your classes wrong.

  • Mark

    Ok. I am not sure how the JVM is allowing classes of one type in a Generic Collection of another. That being said you should not use inheritance just for the case of convenience. If the truly are subclasses of the same parent then what you should do is have one collection in the User object for UserSection. If you need to separate out the subclasses do it in a getUserServiceSection accessor, for example. If you need to call it more than once, build the separate collections once either in the getUserServiceSection on in the setUserSection.

  • Ganeshji Marwaha

    @Doug: With all due respect, If your domain model is simple enough to avoid relationships altogether or if you are not interested in using hibernate to manage the domain relationships, you probably shouldn’t be using hibernate. Tools like iBatis can be your light-weight friend to abstract the Dao layer if you don’t want an ORM managing relationships. I use it pretty often when I need more control. Every technology has its advocates and critics and hibernate is no exception. I believe hibernate eases my burden 80% of the time, and the rest can be handled efficiently through experience.

    Typically, I try and avoid ManyToMany mappings for any new applications, but when we are building applications on top of an existing schema, it is often not possible. I hear ya… I could avoid hibernate when working with legacy schema and I agree.

    But as far as Inheritance goes, it is not always possible to avoid them. Although good design practice suggests refactoring inheritance into delegation, many times there is such a natural hierarchy that refactoring into delegation is not intuitive anymore. So, I often find myself using inheritance mapping.

  • Ganeshji Marwaha

    @Mark #2: Sure, I believe in the right tool for the job. For most new applications developed with top-down approach hibernate is almost always a good choice. Using hibernate without modeling relationships is not the right approach though!!!

    What do you mean, I modeled my classes wrong? Show me where… They look pretty alright to me. Since this is not more of “why I modeled my classes this way” post, i left out the details. Otherwise, I am pretty sure, you will agree.

  • Ganeshji Marwaha

    @Mark #3: “I am not sure how the JVM is allowing classes of one type in a Generic Collection of another” – Wow mark, what is this supposed to mean?

    I am not using inheritance just for the case of convenience. I use inheritance only when there is a natural hierarchy. In this case, tell me how would you prevented code duplication in the domain without inheritance. I know there are ways, but the classes wouldn’t look intuitive anymore…

    I understand your approach.. But is there any particular reason why do choose this round-about way?

  • Does not matter how you call it bug or insane default (feature!), unfortunately @ForceDiscriminator is still needed at Hibernate 3.5 Beta1. Just have tried it.

  • Eddie

    Ganeshji

    I’m with you: pro Hibernate but why is it persisting data for its own use?

    If you take all the defaults yo can get away with just:
    @Inheritance(strategy = InheritanceType.SINGLE_TABLE).

    Hibernate creates a column ‘dtype’ and uses a value of the class name as a discriminator.

    The default discriminator column, not appearing in your pojo properties, appears ghost like haunting a few Queries (Okay: what’s SQL doing in our OO code?).

    Eddie

  • Ganeshji Marwaha

    @ali65 #7: Yes, I tried it too after you mentioned it. Either they are blissfully unaware of this, or they feel that it is a great default. I haven’t tried, but remember my colleague telling me that the default is different when using hibernate XML configuration. Too lazy to try that out now.

  • Ganeshji Marwaha

    @Eddie #8: I prefer being explicit coz, I work with many technologies and cannot always remember what JPAs default is. I agree some of us like to depend on defaults, but that is a matter of taste.

    That said, even otherwise, I would anyday prefer to have a specific discriminator value, instead of having a class name dangling in my database. That way, the same database can be used by other applications with a different class structure (or no domain model at all) and we wont force those developers to depend on our qualified class name. Now, don’t even get me started about refactoring the class name or the package structure later.

    I agree, SQL has nothing to do with OO code. Assuming you configure the same using XML, your argument is no more valid. Isn’t it? Also, Annotations are metadata, they are just pieces of information decorating our OO code. They are not OO code by themselves.

  • Alexandra

    Well said Ganeshji. I too believe that we should not depend on the defaults all the time. A right balance is useful and Being explicit not only makes the code self-documenting, but also helps junior developers know what is being done there.

    I personally prefer XML configuration, So I agree with Eddie that the annotations should not contain SQL details.

  • I have asked them about this. Please comment, please vote!
    http://opensource.atlassian.com/projects/hibernate/browse/ANN-862

  • Ganeshji Marwaha

    @Alexandra #11: That is true. A right balance between being too explicit and relying too much on the default is necessary. It would be too much of an overkill to specify the names of the foreign keys etc in the mapping itself, instead it is better to rely on the defaults provided by JPA/Hibernate, but specifying the things like @DiscriminatorColumn may improve how other applications can use the same database.

    I agree, some developers still prefer XML configuration specifically for ORM mapping, because they believe it is a concern that should be separated out from the code. I use both approaches, but am inclined towards annotations, coz I find it very intuitive to use.

  • Ganeshji Marwaha

    @ali65 #12: You have done a good thing. Thanks. Voted!!!

  • Pingback: Hibernate: Why should I Force Discriminator? | Ganesh « Domain Namez()

  • Cool article. THANKS.

  • Recently, when I was working for nboomi.com, I faced an issue when mapping a OneToMany relationship to the sub-classes of “Single Table Inheritance” strategy.

  • Hi,
    I just walked the same path for the exact same problem, too bad I didn’t find your article first.

    My logical conclusion is that the default behavior is an optimization when you navigate the object graph : it supposes that the foreign ID you are coming from is *unique* across the instances of the mixed types, then it works (it avoids to apply the formula for nothing).

    Still with the default behaviour if you query one of the subclass directly, it will apply the formula because in this case it has only this mean to discriminate them.

    IMHO the default behaviour should be the safe one and an option like @AssumeDiscriminatedIDs could be available for the current one.

  • Alexandra

    hey ganesh, i am missing your insightful blogs, you r one hell of a unique blogger and it is very sad that u dont blog more often! do become active. love alex.

  • Ganesh, What is the best way to contact you directly regarding a feature request on another one of your projects? We might be willing to pay to get the feature implemented, thanks!

  • Ganeshji Marwaha

    @Mike – If you can post your email id here, I will send you an email… We can take it from there…

  • thanx for article. ? exact my problem with this article. please blog more often.

  • thank you very nice article and project…

  • Vinod

    I am just guessing here … maybe the default is the way it is, cuz Hibernate folks might have thought that the default naming strategy will be the one thats going to be used more commonly when a new schema is being worked on and thus the table/column names are generated automatically by Hibernate. The need to explicitly map attributes to columns is something they probably thought could be supported by the ‘Force Discriminator’ annotation while deciding on the default for cases where Hibernate might be used against a pre-existing schema.

  • thanx for article. ? exact my problem with this article. please blog more often

  • Kalyan Dasika

    Ganeshji,

    Good post. We had a similar problem but not quite what you have. In our model, we have another entity that manages the persistence of the subclasses, but we do have a requirement to map the subclasses to the User (Patient in our case).

    I think the ForcedDisciminator is similar to adding a where clause in your mapping (if you are using an old version of hibernate).

    I’m going to try it out and see if it works..

    – Kalyan Dasika

  • Kalyan Dasika

    Ganeshji,

    I put a where clause (match the discriminator value) in the mappings and it worked pretty good.

    Now, why should “I” do it.. its simple in my case,
    1. this project uses non-annotation era version of hibernate so I have no other option but to set up the manual where clause

    2. I believe it will significantly improve the runtime performance when loading a patient profile e.g.. earlier we were loading all events and applying predicates to filter. Now all that is history… and guess I’m now a happy camper.

    Thanks once again for the post da.

    – Kalyan Dasika

  • Ganeshji Marwaha

    @Kalyan #27: Applying a “Where Clause” Filter is another solution to the same problem, but something I would try to avoid, because that way you are directly appending strings to the SQL query generated by Hibernate. In case of a simple “where” clause, it might not be a problem, but when that becomes a bit more than standard SQL, then you don’t have portability anymore. I agree, portability is most often not necessary for medium to big applications, but still, u know what i mean. Another issue I see is – the “where” clause can possibly hold plain strings on the RHS without being used as a prepared statement.

  • Agus

    So many thanks for this post!!!!

    I was struggling with the same “feature” and getting mad.

    I consider this behavior a bug as it forces me to use a Hibernate-specific annotation (@ForceDiscriminator is not a JPA standard) and it seems the behavior is different when using XML configuration instead of annotations.

    Having said that, I must admit I’m generally satisfied with Hibernate as an ORM tool.

    Agus

  • I put a where clause in the mappings and it worked pretty good.

  • redoc

    I got a similar exception with seeded data (through batch process) in db. Instead of discriminator ‘USER’, the discriminator value is ‘User’, so it was throwing exactly the same above exception. When I changed and updated it worked perfectly fine. So watch out for that possibility as well.

  • Abhiranjan

    Hi Ganesh,
    Nice article. I’m facing a similar problem.
    While trying to implement the table per class inheritance using discriminator, I am using a column as the discriminator which is essentially a foreign key referring to a code table.
    I believe I’m doing the mapping, and the discriminator value correct, but when I try to save, it gives me an error meaning
    – cannot insert null in the discriminator column (foreign key column). Is it an issue with the discriminator column being foreign key too?

  • Hibernate why should i force discriminator.. Nice 🙂

  • Hibernate why should i force discriminator.. Retweeted it 🙂

  • Very interesting many thanks, I presume your readers would likely want more reviews along these lines continue the great effort.

  • Gunjan

    Awesome! Thanks – solved my problem..

  • Pingback: Hibernate n’aime pas la discrimination | Excilys Labs()

  • I like your blog applications.This is one of the important post.Great work..

  • I won’t dive into the discussion here and read quickly the HHH-JIRAs.

    Thanks so much for this post – It solved my issue on 3.5.6.
    Why it is required? I don’t care for the moment to be honest (I’m not restricted to be only-JPA2)