JPA – Hibernate – Type mapping on package level
When we are finally mature enough to use some custom types mapping in JPA, we usually stuck with some provider specific solution, because JPA itself doesn’t define any mechanism for doing it. Let me show you an example of custom type mapping definition for one of the JPA providers – Hibernate. Suppose that we use Joda Money in our project, and have an entity with property having type Money. There are already pretty nice type mapping implementations for Money, provided by Jadira – User Types project. All we have to do is just let Hibernate know that we want to use specific type mapping. When you look at the Hibernate Docs, Section 5.1.4.1.1: Type, you’ll see few possibilities, starting from the simplest – using @Type annotation on each property having Money type. This choice can be good if you have only one, or very few, properties of this type in your domain mapping. It is very probable that sooner
or later, when your project will grow enough, there will be more and more of them, and you end up with many similar lines defining the same type mapping. If you aren’t a big fan of repeating yourself, or you don’t trust in refactorings made by your apprentices, you should consider another way, using @TypeDefs and @TypeDef annotations. As you may read in Hibernate documentation:
‘These annotations can be placed at the class or package level.’
Let’s focus on the second option – package level. We will place these annotations in package-info.java for our domain entities holding package (see: Java Language Specification – 7.4.1. Named Packages). It will look like this:
/** * Provides the domain model. * * @author Warlock */ @org.hibernate.annotations.TypeDef(name = "money", defaultForType = Money.class, typeClass = PersistentMoneyAmount.class) package com.blogspot.vardlokkur.domain; import org.jadira.usertype.moneyandcurrency.joda.PersistentMoneyAmount; import org.joda.money.Money;
Now, when you map the property using Money type, you can do it without additional type mapping specification, just like this:
package com.blogspot.vardlokkur.domain; ... import org.joda.money.Money; @Entity @Table(name = "EMPLOYEE") public class Employee implements Serializable { ... @Column(name = "SALARY") private Money salary; ... }
One technical note, before you become happy Money mapping user – Because PersistentMoneyAmount uses single column (holding amount) for Money mapping, it requires defining of currency which will be used along with the amount. The default currency can be defined as Persistence Unit property: jadira.usertype.currencyCode
PS. Don’t treat the above Money example as the guideline of Joda Money mapping, there are probably better ways of doing it, see Jadira User Types blog.
Few links for the dessert:
Could you break down this line of code in more detail:
@org.hibernate.annotations.TypeDef(name = “money”, defaultForType = Money.class, typeClass = PersistentMoneyAmount.class)
What does does defaultForType do?
What does typeClass do?
The salary property of the Employee class is of type Money, so how does that relate to PersistentMoneyAmount?
PersistentMoneyAmount in Michal’s example is a Hibernate mapping type. It is an implementation of Hibernate specific contracts, usually org.hibernate.type.Type or org.hibernate.usertype.UserType. Those contracts allow Hibernate to convert between Java data and JDBC data, as well as letting it manage value comparisons, value caching, etc. defaultForType is a directive telling Hibernate to use this type defintion as the default for all attributes exposed using the indicated type (Money.class in the example). In other words, with that definition in place, anytime Hibernate sees a persistent attribute whose exposed type is Money, this “money” type definition would be used for the mapping. That… Read more »
@org.hibernate.annotations.TypeDef(name = “utcType”, defaultForType = java.util.Date.class,
typeClass = com.foo.db.hibernate.UTCTimestampType.class)
package com.foo.db.model;
I put the annotation on the package but the type is not being picked up. My type is for making Hibernate set dates in the database to UTC and retrieve dates in UTC. That way it does not depend on the server time zone or JVM time zone. Any ideas why Hibernate wouldn’t pick this up?
I would like to note as well putting the @Type on Date objects in my models works, but this would be tedious as mentioned in the article.
@google-31f26d1d239610fa624cc3c4f3dae813:disqus – defaultForType defines the type of property for which this @TypeDef is targeted; typeClass defines Hibernate type which will be used (see also http://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html_single/#mapping-types-custom )
In my example the type of property for which we define mapping (using @TypeDef annotation) is Money class, and PersistentMoneyAmount class (which implements Hibernate UserType interface) is responsible for converting database representation of money into Money class instances (and vice versa).
At the end of the post, the p.s. alludes to a better way of handling it using Jadira.
Just to fill in what that better way is…
Put this in persistence.xml
Now a simple @Column annotation will work:
@Column(nullable=false)
private DateTime updated;
I just went though figuring that out… jadira is good, but the documentation isn’t all that well organized.