Hibernate Associations: targetEntity

Hibernate provides quite a lot of power and features when it comes to mapping domain objects to relational tables. However, if used incorrectly, some of these nasty (in a good way, of course) little features could drive you crazy in a the rush of work.

One such powerful, but dangerous (if mis-used) feature of Hibernate associations is the support for specifying alternative types for mappings, instead of the type specified in the field itself. While this feature is quite useful in certain cases, mis-use of this could lead to hard to find bugs.

In one of my recent projects, one of my integration tests for a complex module was failing with the following exception stack-trace:

could not get a field value by reflection getter of com.sample.maintenance.core.domain.Country.id;
nested exception is org.hibernate.PropertyAccessException:
could not get a field value by reflection getter of com.sample.maintenance.core.domain.Country.id
at org.springframework.orm.hibernate3.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:676)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:95)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:212)
at org.springframework.orm.jpa.JpaAccessor.translateIfNecessary(JpaAccessor.java:152)
at org.springframework.orm.jpa.JpaTemplate.execute(JpaTemplate.java:189)
at org.springframework.orm.jpa.JpaTemplate.flush(JpaTemplate.java:293)
at com.sample.persistence.platform.AbstractBaseJpaDaoSupport.saveOrUpdate(AbstractBaseJpaDaoSupport.java:329)
at com.sample.persistence.reservation.impl.jpa.ReservationRegistrationDaoImpl.saveOrUpdate(ReservationRegistrationDaoImpl.java:39)
at com.sample.persistence.reservation.impl.jpa.ReservationRegistrationDaoImpl.saveOrUpdate(ReservationRegistrationDaoImpl.java:1)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:307)
... 63 more
Caused by: java.lang.IllegalArgumentException: Can not set java.lang.Long field com.sample.maintenance.core.domain.Country.id to com.sample.maintenance.core.domain.Nationality
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:146)
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:150)
at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:37)
at sun.reflect.UnsafeObjectFieldAccessorImpl.get(UnsafeObjectFieldAccessorImpl.java:18)
at java.lang.reflect.Field.get(Field.java:358)
at org.hibernate.property.DirectPropertyAccessor$DirectGetter.get(DirectPropertyAccessor.java:55)
... 95 more

Obviously, there seems to be a issue with the ID field of Country entity, and the Nationality entity. I went through the mappings of each of these entities from top to bottom and from bottom to up, but came up with nothing! Mappings were accurate, and integration tests which uses these entities straight up (CRUD operations) were 100% successful.

After wasting about an hour, I found the culpirit which was causing the issue. The mapping for a field in another entity which associates Nationality entity was mapped incorrectly by somebody, specifying it’s target entity as Country. This is shown below:

@ManyToOne(targetEntity = Country.class, fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "COUNTRY_ID")
private Country country;
@ManyToOne(targetEntity = Country.class, fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "NATIONALITY_ID")
private Nationality nationality;

But the exception trace was mis-leading (well, that depends on experience, and this was my first time experiencing it) and caused me to waste an hour during the rush. In any case, the targetEntity attribute is no use for the above code, because it’s real use is to specify an alternative type for the type that can be inferred through Reflection. Since in the above case the both is same, the attribute has no effect.

We had to learn the lesson the hard-way :).


Leave a Reply

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