Composited objects with shared id’s in Hibernate

One of the models a developed in a previous project used embedded/embeddable annotations to create a composite object. The (simplified) object model looks like this:

avattributes object model

The embedded/embeddable solution would store the entire graph in a single table, all attributes flattened to one row:

avattributes_table

That works great as long as you don’t mind minor annoyances like:

  • when loaded, embedded object are filled with null values if no attributes are present in the database.
  • you have no constraints which need to be satisfied only if an entire object exists (ie. if I set videoattributes, the null value is required)

Or in other words: only do this if the objects are always supposed to be present. Which in the case above is simply not true. If you would like to describe audio-only content you don’t want to set the ‘required’ video codec.

My current project is a spin-off of the project mentioned above and uses some concepts of the model on a new database. I decided to alter the mappings and give Audio- and VideoAttributes their own table. This proved to be somewhat harder then I expected:

  • alter the embeddable annotation of Audio- and VideoAttributes, make them entities
  • since Audio- and VideoAttributes are entities now the need an id
  • add a reference to the AVAttributes object to Audio- and VideoAttributes
  • create a bi-directional one-to-one mapping between avattributes and audio- and VideoAttributes. mappedBy on the Audio- and VideoAttributes side
  • add cascade annotations to the Audio- and VideoAttributes in the AVAttributes class

I started out with generated id’s for the Audio- and VideoAttributes. Hibernate will generate two columns in the AVAttributes containing a foreign keys to the Audio- and VideoAttributes tables. This synthetic id seemed a bit unnecessary IMHO. It took me a while to figure out how to do this. I ended up with the following annotations:

AVAttributes
[java]
@Entity
public class AVAttributes implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

@OneToOne(optional = true)
@PrimaryKeyJoinColumn
@org.hibernate.annotations.Cascade({
org.hibernate.annotations.CascadeType.ALL,
org.hibernate.annotations.CascadeType.DELETE_ORPHAN
})
private AudioAttributes audioAttributes;

@OneToOne(optional = true)
@PrimaryKeyJoinColumn
@org.hibernate.annotations.Cascade({
org.hibernate.annotations.CascadeType.ALL,
org.hibernate.annotations.CascadeType.DELETE_ORPHAN
})
private VideoAttributes videoAttributes;
}
[/java]

AudioAttributes (Same annotations used for VideoAttributes)
[java]
@Entity
public class AudioAttributes implements Serializable {
@Id
@GeneratedValue(generator=”fk”)
@GenericGenerator(name=”fk”,strategy=”foreign”,parameters = {
@Parameter(name=”property”,value=”avAttributes”)
})
private Long id;

@OneToOne(mappedBy = “audioAttributes”, optional = false)
private AVAttributes avAttributes;
}
[/java]

The generated table structure looks like this:

avattributes_split_table

Id’s are automatically cascaded when a AVAttributes object is persisted. And yes, it’s a lot of annotations and I even stripped out the JAXB2 annotations which are also present.

This entry was posted in java. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>