views:

961

answers:

2

Good day.

This is a Hibnerate polymorphism question and a data model design question; they are intertwingled. I've used Hibernate in the past, and have enjoyed it, but sometimes I find it difficult to think about anything but trivial designs. Not a knock on Hibernate; just an observation that ORM in general can be challenging.

I think this is a Hibernate 101 question, but I am not sure. What I am trying to achieve may not even be possible.

I have an abstract class Fruit that will be subclassed into Apple and Orange. I have a Note class that represents notes or comments about Apples and Oranges. An Apple or Orange can have many Notes associated with it, but only one Apple or Orange will ever be associated with a given Note.

Here are sketches of the classes, where I am for now omitting where the object id's go, and the properties of Apples that distinguish them from Oranges. For the time being, I don't feel strongly about which Hibernate inheritance strategy I use.

abstract public class Fruit {
}

// Apples have notes written about them:
public class Apple extends Fruit {
   private Set<Note> note;
   ...

   @OneToMany(cascade = CascadeType.ALL)
   public Set<Note> getNote() {
       return note;
   }
}


// Oranges have notes written about them:
public class Orange extends Fruit {
   private Set<Note> note;
   ...

   @OneToMany(cascade = CascadeType.ALL)
   public Set<Note> getNote() {
       return note;
   }
}

Here is the Note class currently implemented, wherein we see that it has fields for both an Apple and an Orange. The flaw or inelegance in this design is that a single note instance will only point to one of Apple or Orange, and never both. So if a Note is bound to an Apple, the Orange field is superfluous and unsightly, and viceversa.

// A note about an Apple or Orange
public class Note {
   private String theNote;
   private Apple apple; 
   private Orange orange;
   ...

   // with the usual many to one mapping
   @ManyToOne
   @JoinColumn(name = "apple_id")
   public Apple getApple() { 
       return apple;
   }

   // with the usual many to one mapping
   @ManyToOne
   @JoinColumn(name = "orange_id")
   public Orange getOrange() {
       return orange;
   }

   ...
}

However, this is the Note class that I think I want to base my design on, but I'm not sure how to think about this with respect to Hibernate annotation and table mapping:

// A note about a fruit:
public class Note {
   private String theNote;
   private Fruit fruit;
   ...
}

whereupon fruit will be either an Apple or Orange instance.

Can this latter Note class, with its reference to a Fruit, which will actually hold an Apple or Orange, even be reconciled with Hibernate ORM mapping? If yes, can someone please talk about how.

Thanks much.

+3  A: 

This is absolutely possible. You could associate the notes with the abstract Fruit class instead of repeating them in each of the implementations:

@Entity
@Inheritance
public abstract class Fruit {
   private Set<Note> notes;
   ...

   @OneToMany(cascade = CascadeType.ALL, mappedBy = "fruit")
   public Set<Note> getNotes() {
       return notes;
   }
}

@Entity
public class Apple extends Fruit {
   ...
}


@Entity
public class Orange extends Fruit {
   ...
}

@Entity
public class Note {
   private String theNote;

   @ManyToOne
   private Fruit fruit;
   ...
}

Ét voilà!

-- Addition based on comment: JPA provides multiple strategies for dealing with inheritance. The relevant section in the Java EE tutorial should help you get started.

Basically, your options are:

  • Storing everything in one table and using a discriminator column to know which row is which type
  • Storing each concrete class (Apple and Orange) in a separate table
  • Having a common Fruit table with a discriminator column, and Apple and Orange tables with a foreign key to the Fruit table

Another edit: Noticed this is a Hibernate, not a JPA question. Does not make too much of a difference, though, as the options are the same. Here's the relevant section in the Hibernate docs.

Henning
Temporary -1: this doesn't answer the question, does it? You've told Hibernate about the relationship between Fruit and Note. How do you tell it how to disambiguate a Fruit to either an Apple or an Orange?
Jason S
@ Jason: Actually, it does, and very literally. If you read the question again, particularly the last paragraphs starting from the second code block, the code I'm providing is exactly what ae6rt is asking for.
Henning
-1 removed... could you elaborate as to how an Apple gets stored in the database differently than an Orange?
Jason S
+1: thanks, that helps clear things up. It looks like the discriminator column + per-table subclass is the way I want to go in my case. found this link for more examples: http://www.javalobby.org/java/forums/t18300.html
Jason S
Thanks, Henning.
ae6rt
+1  A: 

This pattern is very common in Hibernate based datalayers. How it works internally is heavily based on which inheritance strategy is used.

When using table per class or table per subclass inheritance, a table for every subclass/class will be created. For example, in your case, you would have a table Fruit and two tables Apple and Orange, with foreign key references between Fruit and Apple/Orange. When requesting a single fruit (be it apple or orange) by ID, Hibernate will outer join the Fruit table with the Apple and Orange table. Each row will be transformed into an Apple or Orange depending on from which table the fields were retrieved.

Another possibility is to use discriminators. A single table Fruit will be used which will contain a discriminator field (ex. fruit_type taking values apple and orange). Depending on the value of this field, Hibernate will determine whether the corresponding object is an Apple or an Orange.

In your case, in the case of eager loading, when Hibernate loads the Note object it will eagerly fetch the corresponding Fruit and populate the fruit field with an instance of Apple or Orange accordingly.

In the case of lazy fetching, the fruit field will be a proxy implementing the Fruit interface. Until the actual fruit field is loaded its type is undetermined.

Hope this answers some of your queries.

Il-Bhima