javahibernatejpadata-modelingmulti-table-inheritance

Hibernate: How to model an Inheritance type structure and do operations without explicit casting


I have an application that consumes incoming messages, parses the data present in the message, and then applies rules to that data. On the Rule entity, there's a column that distinguishes the type of rule.

I want to persist the Result of the rule to separate tables or subclasses, depending on what type of Rule processed them.

I'm currently solving this by creating a parent @MappedSuperclass (abstract) BaseResult object and an AppleResult and OrangeResult @Enitiy that extends the BaseResult.

My question is, given the statement below, how can I improve/annotate my objects in the model so that I don't have to do an instanceof check for each instance as I go to access/persist it? Right now this is what I'm having to do to avoid "baseresult does not exist" SQL grammar exceptions:

public void save(BaseResult baseResult) {
    if (baseResult instanceof AppleResult) {
        jpaApi.em().merge((AppleResult) baseResult);
    } else if (baseResult instanceof OrangeResult) {
        jpaApi.em().merge((OrangeResult) baseResult);
    }
}

I'm hoping there's a more elegant solution than having to do a if/else and explicitly cast depending on the result. I've looking into using things like @DiscriminatorValue annotation of using Generics, but these all seem to require that the BaseResult in my case is also an entity, which it's not.


Solution

  • You should use @Inheritance. Then, saving would be as simple as:

    public void save(final BaseResult baseResult) {
        jpaApi.em().merge(baseResult);
    }
    

    Which inheritance strategy to use depends on your current database design, but I'm guessing that you have a table for each of the subclasses, so something like this:

    @Entity
    @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
    public abstract class BaseResult {
        //...
    }
    
    @Entity
    public class AppleResult extends BaseResult {
        //...
    } 
    

    Having @Entity on the superclass is not a problem, as it is abstract anyways..

    Also, using merge is usually something one shouldn't do, you should just manipulate your entities within a transaction and it's automatically persisted in the database upon commit of the transaction:

    @Transactional //either this...
    public void doStuff(final ResultCommand command) {
        //begin transaction <-- ...or this
        final BaseResult result = em.find(BaseResult.class, command.getResultId());
        result.apply(command);
        //end transaction
    }