Mixing EJB3 discriminators and the JOINED strategy: Hibernate falls short

Has anyone out there ever gotten a decent response from filing a Hibernate bug? I sure haven’t. Here’s my latest one, on the subject of support for JOINED entities with discriminator columns in Hibernate’s EJB3 implementation. I ran into a situation that was handled fine in other EJB3 implementations but not Hibernate. In his inimitable fashion, Gavin King claims it’s not really a problem “because Hibernate is better than these other inferior implementations.” But I think he’s wrong.

Suppose we have the following set of EJB3 entity beans. This is a completely legal setup and it works just fine in other EJB3 containers. As a matter of fact, the EJB3 documentation that comes with Resin has a comparable example.

@Entity(access=FIELD)
@DiscriminatorColumn(name="pubType")
@Inheritance(strategy=JOINED)
public abstract class Publication {
   @Id
   public int id;
   public String title;
   @ManyToOne
   public Publisher publisher;
}

@Entity(access=FIELD)
@Inheritance(discriminatorValue="poster")
public class Poster extends Publication {
   public int width;
   public int height;
}

@Entity(access=FIELD)
@Inheritance(discriminatorValue="book")
public class Book extends Publication {
   public String author;
   public String isbn;
}

So far so good. Hibernate can deal with this much — Gavin is correct that in this kind of situation, we don’t actually need the discriminator column since each subclass can be distinguished by the fact that it has a non-null ID value when you outer-join the three tables involved.

Now suppose we add these classes to the mix:

@Entity(access=FIELD)
public abstract class Periodical extends Publication {
   public int issuesPerYear;
   public boolean isSoldAtNewsstands;
   public boolean hasSubscriptionDiscount;
}

@Entity
@Inheritance(discriminatorValue="magazine")
public class Magazine extends Periodical { }

@Entity
@Inheritance(discriminatorValue="newspaper")
public class Newspaper extends Periodical { }

Notice that there are no properties in the two new concrete subclasses here, though there are some for the abstract class in the middle of the hierarchy; these will cause the EJB3 container to join to a table "periodical" using the default table naming rules laid out in the EJB3 spec. Three new classes, one new table to join to.

What does this mean for Hibernate, the so-called "better" ORM system that has no need of such trifles as discriminator columns? Well… in this case, it needs a discriminator column. A simple outer join has no way of distinguishing between a Newspaper and a Magazine since those classes have no join tables of their own. Hibernate can certainly tell that it’s getting a bunch of Periodicals when it joins the four tables in question — but a Periodical is not a valid entity in the class hierarchy.

This example is not just for the sake of argument; two of the customers I’m consulting for right now have this kind of class hierarchy. I’m guessing they aren’t unusual, judging by the fact that similar examples occur in most of the EJB3 tutorials I’ve seen.

Hibernate’s EJB3 implementation can support this kind of object model. It looks like this:

@Entity(access=FIELD)
@DiscriminatorColumn(name="pubType")
@Inheritance(strategy=SINGLE_TABLE)
public abstract class Publication {
   @Id
   public int id;
   public String title;
   @ManyToOne
   public Publisher publisher;
}

@Entity(access=FIELD)
@Inheritance(discriminatorValue="poster")
@SecondaryTable(name="poster")
public class Poster extends Publication {
   @Column(secondaryTable="poster")
   public int width;
   @Column(secondaryTable="poster")
   public int height;
}

@Entity(access=FIELD)
@Inheritance(discriminatorValue="book")
@SecondaryTable(name="book")
public class Book extends Publication {
   @Column(secondaryTable="book")
   public String author;
   @Column(secondaryTable="book")
   public String isbn;
}

@Entity(access=FIELD)
@SecondaryTable(name="periodical")
public abstract class Periodical extends Publication {
   @Column(secondaryTable="periodical")
   public int issuesPerYear;
   @Column(secondaryTable="periodical")
   public boolean isSoldAtNewsstands;
   @Column(secondaryTable="periodical")
   public boolean hasSubscriptionDiscount;
}

@Entity
@Inheritance(discriminatorValue="magazine")
public class Magazine extends Periodical { }

@Entity
@Inheritance(discriminatorValue="newspaper")
public class Newspaper extends Periodical { }

Which one is more elegant? You be the judge — maybe you think being forced to add a @Column(secondaryTable="x") annotation to every property of every subclass is a sure sign of a superior ORM system. Gavin King seems to, and I suppose it’s just my opinion against his. If we take him at his word, I guess the definition of "better" includes "unable to successfully run simple spec-compliant code that works fine in other vendors’ containers."

Leave a Reply