Hibernate 4: persisting InheritanceType.JOINED discriminator column values

Kawu picture Kawu · Nov 3, 2011 · Viewed 23.7k times · Source

I have a simple JOINED hierarchy of documents:

CREATE TABLE Documents
(
  id INTEGER NOT NULL,
  discriminator ENUM('official','individual','external') NOT NULL,
  file_name VARCHAR(200) NOT NULL,
  PRIMARY KEY (id)
);

CREATE SystemDocuments
(
  id INTEGER NOT NULL,
  binary_data BLOB NOT NULL,
  PRIMARY KEY (id),
  FOREIGN KEY (id) REFERENCES Documents (id)
);

CREATE ExternalDocuments
(
  id INTEGER NOT NULL,
  PRIMARY KEY (id),
  FOREIGN KEY (id) REFERENCES SystemDocuments (id)
);

As you can see all the sub tables do is share the same ID from the Documents table. Other than that SystemDocuments adds a binary_data column and ExternalDocuments adds no new properties. (Also note there are two other concrete sub tables in the hierarchy denoted by 'official' and 'individual' which are of no relevance here.)

Here are the mappings for the above tables:

Document.java:

@Entity
@Table(name = "Documents")
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "discriminator", discriminatorType = DiscriminatorType.STRING)
//@DiscriminatorOptions(force = true) // <-- Hibernate 4-specific annotation not inserting discriminator values
public abstract class Document implements Serializable
{
    @Id
    @Column
    protected Integer id;

    @Column(name = "file_name")
    protected String fileName;

    ...
}

SystemDocument.java:

@Entity
@Table(name = "SystemDocuments")
public abstract class SystemDocument extends Document
{
    @Lob
    @Column(name = "binary_data")
    protected byte[] binaryData;

    ...
}

ExternalDocument.java:

@Entity
@Table(name = "ExternalDocuments")
@DiscriminatorValue(value = "external")
public class ExternalDocument extends SystemDocument
{
    ...
}

The latter class is supposed to be mapped to the Documents' discriminator column value 'external'. When finding entities via EntityManager.find the discriminators are returned correctly, well actually because the discriminators of my test data have been INSERTed into the DB correctly.

Now I use the following code to insert new documents/files into the system via JPA and a file uploader:

...

UploadedFile uf = event.getUploadedFile();

// set ID, file name, and binary data
ExternalDocument detachedExternalDocument =
    new ExternalDocument(1234567, uf.getName(), uf.getData());

docService.create(detachedExternalDocument);

When inspecting the DB however I can see that Hibernate does not insert the 'external' discriminator value into the Documents table's discriminator column.

There have been issues about this in the past, see https://hibernate.onjira.com/browse/ANN-140 and more recently for Hibernate 4 https://hibernate.onjira.com/browse/HHH-4358, so chances are it's supposed to work that way.

I then found http://docs.jboss.org/hibernate/core/4.0/javadocs/org/hibernate/annotations/DiscriminatorOptions.html in the current Hibernate 4 API Docs, but it doesn't work (see @DiscriminatorOptions at the Document class).

How can I get Hibernate 4 to insert the discriminators using raw annotations?

Note: I don't want to map the discriminator column as a regular column.

Answer

Kawu picture Kawu · Dec 20, 2011

First of all, this question is a duplicate of Discriminator in InheritanceType.JOINED.

It seems like persisting discriminator values in JOINED inheritance is not required by the JPA spec. Here's what I've received from a member of the JPA expert group via email:

The spec does not require an implementation to use discriminator columns to implement JOINED inheritance, however, the assumption is that if @DiscriminatorColumn is specified then it would be used, i.e. the values would be written out. We do not explicitly state that if a @DiscriminatorColumn is specified in the code it must be used, just like we don't explicitly state that if a @Column or @JoinColumn is specified the values must be stored in the table, but there is only so much that we can or should specify. At the lowest level, certain laws of physics and reason are just assumed.

The problem at hand has been an issue with Hibernate for quite a while, see here:

https://hibernate.atlassian.net/browse/ANN-140

Rejection comment:

EJB3 does NOT require use of discriminators with JOINED mapping strategies. It is allowed for inferior implementations of the JOINED mapping strategy which require a discriminator. Hibernate does not need a discriminator because Hibernate is better than these other inferior implementations.

In the end only SINGLE_TABLE strategy requires a discriminator column, JOINED can be implemented without. The problem with Hibernate currently is that it causes inconsistent data when persisting sub entities in a JOINED inheritance mapped with @DiscriminatorColumn, even though the JPA spec recommends to persist discriminator values if a discriminator is used with JOINED. See more in the RFE here:

https://hibernate.atlassian.net/browse/HHH-6911