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:
The embedded/embeddable solution would store the entire graph in a single table, all attributes flattened to one row:
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:
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.


