DiscriminatorFormula

John Duff picture John Duff · Sep 30, 2011 · Viewed 9.7k times · Source

If I have a single table where I need multiple columns for discriminator, is my only solution to use a @DiscriminatorFormula?

I ask because some prototyping gave us some results that I cannot explain.

Failed Prototype: Initially we prototyped a 3-deep class hierarchy using one @DiscriminatorColumn on the super class and included the second @DiscriminatorColumn on subclasses. Of course we got this warning:

Discriminator column has to be defined in the root entity, it will be ignored in subclass

We found that updates worked, but inserts did not. So we scrapped that idea.

Successful? prototype: We then tried the following which seems to work: Omit the second @DiscriminatorColumn on the subclasses and just include the @JoinColumn on a foreign key (which we were going to need anyway). Perhaps, since the joins are to different types of objects, Hibernate/JPA seems to be able to figure out which subclass is correct. Can anyone explain that?

Should we scrap that and just use @DiscriminatorFormula to get the explicit relationship defined on the 2 discriminator columns?

Answer

Laura Liparulo picture Laura Liparulo · Nov 5, 2014

DiscriminatorFormula is an alternative to DiscriminatorColumn. You annotate the superclass (which maps the real table as default) with one of them. With DiscriminatorColumn it creates an additional column (as default called "dtype") which contains the discriminator value. You put the annotation in the superclass:

@Entity
@Table(name = "features")
@DiscriminatorColumn
public class Features{
    //valid code
}

DiscriminatorFormula allows you to check the database row content and "select" a subclass by the discriminator value. No additional ("dtype") column is created. In the main class you annotate the superclass with the formula, like:

@Entity
@Table(name = "features")
@DiscriminatorFormula(
        "CASE WHEN num_value IS NOT NULL THEN 'NUMERIC' " +
        " WHEN txt_value IS NOT NULL THEN 'TEXT' end"
)
public class Features{
    //valid code
}

In the DiscriminatorFormula you just put some pure SQL to do what you need.

You can choose one of these two options and the subclasses is exactly the same in both cases. In the subclasses you specify the discriminator value, for Example:

@Entity
@DiscriminatorValue('NUMERIC')
public class NumericFeatures extends Features {

    private Double numValue;

    public Double getNumValue() {
        return numValue;
    }

    public void setNumValue(Double numValue) {
        this.numValue = numValue;
    }

    //valid code
}

in the table called "features" you have both columns "num_value" and "txt_value", containing the corrensponding values. With DiscriminatorColumn you would have either "NUMERIC" or "TEXT" value in the additional dtype column and also both the "num_value" and "txt_value" columns.

If you don´t specify the inheritance strategy the default is "SINGLE_TYPE". the following annotation can be omitted if it´s your chosen strategy:

@Inheritance(strategy = InheritanceType.SINGLE_TABLE)

With or without this annotation you get one table called "Features".