Hibernate - the @Any Annotation

Hi, 

The 3.3.1.GA release of Hibernate annotations (released on March) includes a new annotation: the @Any (see http://opensource.atlassian.com/projects/hibernate/browse/ANN-28). This annotation (accomplished with @AnyMetaDef) provides the Hibernate annotation and EntityManager support for the "any" type.

The Any Property Type

Sometimes we need to map an association property to different types of entities that don't have a common ancestor entity - so a plain polymorphic association doesn't do the work. For example let's assume three different applications which manage a media library - the first application manages books borrowing, the second one DVDs, and the third VHSs. The applications have nothing in common. Now we want to develop a new application that manages all three media types and reuses the exiting Book, DVD, and VHS entities. Since Book, DVD, and VHS classes came from different applications they don't have any ancestor entity - the common ancestor is java.lang.Object. Still we would like to have one Borrow entity which can refer to any of the possible media type.

To solve this type of references we can use the any mapping. this mapping always includes more than one column: one column includes the type of the entity the current mapped property refers to and the other includes the identity of the entity, for example if we refer to a book it the first column will include a marker for the Book entity type and the second one will include the id of the specific book.


The @Any Annotation Sample

The @Any annotation has to be used with additional two annotation the @AnyMetaDef and the @JoinColumn, here is an example:



@Entity
@Table(name = "BORROW")
public class Borrow{

    @Id
    @GeneratedValue
    private Long id;

    @Any(metaColumn = @Column(name = "ITEM_TYPE"))
    @AnyMetaDef(idType = "long", metaType = "string", 
            metaValues = { 
             @MetaValue(targetEntity = Book.class, value = "B"),
             @MetaValue(targetEntity = VHS.class, value = "V"),
             @MetaValue(targetEntity = DVD.class, value = "D")
       })
    @JoinColumn(name="ITEM_ID")
    private Object item;

     .......
    public Object getItem() {
        return item;
    }

    public void setItem(Object item) {
        this.item = item;
    }

}

Look on the item property above:
  • Notice the reference type: it is a java.lang.Object reference. This fits to my "user story", but actually in other scenarios this could be from a more "interesting" type for example an interface implemented by the three entities.
  • Also see the usage of @Any, @AnyMetaDef, and @JoinColumn

The @Any

The @Any above contains one attribute - 'metaColumn'. This is the name of the column which contains the meta data for the association - the marker for the referenced entity type. The attributes of this meta column are described in the following line using the @AnyMetaDef annotation. The metaColumn attribute is mandatory, there are three optional attributes to the @Any annotation:

The @AnyMetaDef

Maybe this is the core of the any mapping, this annotation describes how the reference meta data will be stored and interpreted by Hibernate, it has three mandatory attributes:
  • idType - the Hibernate type (notice this is an Hibernate type) of the referenced entities id column
  • metaType - the Hibernate type of the discriminator column
  • metaValues - an array of @MetaValue, the metaValue annotation (described below) is used to assign a discriminator to an entity type.
In the example above we can see that the idType (this is the type of the id property in the referenced entities) is long, the discriminator type is String and the mapping from entities to discriminators:
  • Book is mapped to B
  • VHS to V
  • and DVD to D
The @AnyMetaDef annotation has one more attribute - name, we will see its use later.

The @MetaValue

This annotation is very simple, it includes two mandatory attributes the tragetEntity which is the type (class) of the target entity, and the value which is the discriminator value. The discriminator values will be stored in the meta column to identify the exact type being referenced.


That's It

Basically that's it, there are few variants and small prints to notice:
  1. The @JoinColumn annotation is mandatory, using this annotation we define the column that holds the identity of the entity we refer to. Don't worry about forgetting it - Hibernate will remind you on starting up.
  2. Foreign keys - since the join column values are references (foreign keys) to more one table we can't use database foreign keys on that column
  3. Named meta definition - the @AnyMetaDef annotation can be placed at the package level and we can assign a name to it. This type of definition can be reused by referencing the AnyMetaDef name using the @Any's metaDef attribute - a very clear example can be found on Hibernate Annotation documentation (here)

Please notice: I had recover this blog post from my old blog at http://www.jroller.com/eyallupu since jroller.com is no longer available. As such the styling might be a bit wobbly ... If something seems 'too broken' please contact me and I'll adjust

Comments

Popular posts from this blog

New in Spring MVC 3.1: CSRF Protection using RequestDataValueProcessor

Hibernate Exception - Simultaneously Fetch Multiple Bags

Hibernate Derived Properties - Performance and Portability