In my domain model, I have an abstract entity Indicator inherited by two concrete Elementary and Composite.
The abstract entity hold an entity Factor in order to be accessible for the two sub-classes.
The relation is bi-directional. So the entity Factor hold an instance of the abstract entity Indicator.
Of course, the real instance is either Elementary or Composite.
@Entity
@Inheritance(strategy = JOINED)
public abstract class Indicator implements Serializable {
@OneToMany(mappedBy = "indicator")
private List<Factor> factors = new ArrayList<Factor>();
...
}
@Entity
@Audited
public class Factor implements Serializable {
@ManyToOne(optional = false)
@JoinColumn(name = "ID_RSK_IND", nullable = false)
@ForeignKey(name = "FK_FAC__IND")
private Indicator indicator;
}
@Entity
@Audited
public class Elementary extends Indicator {
...
}
@Entity
@Audited
public class Composite extends Indicator {
...
}
I use Dozer to map these entities with themselves in order to "break" the hibernate instrumentation and push them on client side (GWT).
Whith "classic" Hibernate, all works fine : Dozer cross the beans model to duplicate it.
But, when I use the Envers AuditReader for querying versioned entities, I get an InstantiationException. It come from the fact that the instance of Factor try to instantiate an instance of Indicator
09:36:04,702 - ERROR - org.dozer.MappingProcessor - Field mapping error -->
MapId: null
Type: null
Source parent class: com.sg.rrf.l2r.shared.entity.market.indicator.elementary.Elementary
Source field name: factors
Source field type: class org.hibernate.envers.internal.entities.mapper.relation.lazy.proxy.ListProxy
Source field value: [1]
Dest parent class: com.sg.rrf.l2r.shared.entity.market.indicator.elementary.Elementary
Dest field name: factors
Dest field type: java.util.List
org.dozer.MappingException: java.lang.InstantiationException
at org.dozer.util.MappingUtils.throwMappingException(MappingUtils.java:82)
at org.dozer.factory.ConstructionStrategies$ByConstructor.newInstance(ConstructionStrategies.java:280)
at org.dozer.factory.ConstructionStrategies$ByConstructor.create(ConstructionStrategies.java:245)
at org.dozer.factory.DestBeanCreator.create(DestBeanCreator.java:65)
at org.dozer.MappingProcessor.mapCustomObject(MappingProcessor.java:489)
at org.dozer.MappingProcessor.mapOrRecurseObject(MappingProcessor.java:446)
at org.dozer.MappingProcessor.mapFromFieldMap(MappingProcessor.java:342)
at org.dozer.MappingProcessor.mapField(MappingProcessor.java:288)
at org.dozer.MappingProcessor.map(MappingProcessor.java:248)
at org.dozer.MappingProcessor.map(MappingProcessor.java:197)
at org.dozer.MappingProcessor.mapCustomObject(MappingProcessor.java:495)
at org.dozer.MappingProcessor.mapOrRecurseObject(MappingProcessor.java:446)
at org.dozer.MappingProcessor.addOrUpdateToList(MappingProcessor.java:776)
at org.dozer.MappingProcessor.addOrUpdateToList(MappingProcessor.java:850)
at org.dozer.MappingProcessor.mapListToList(MappingProcessor.java:686)
at org.dozer.MappingProcessor.mapCollection(MappingProcessor.java:541)
at org.dozer.MappingProcessor.mapOrRecurseObject(MappingProcessor.java:434)
at org.dozer.MappingProcessor.mapFromFieldMap(MappingProcessor.java:342)
at org.dozer.MappingProcessor.mapField(MappingProcessor.java:288)
at org.dozer.MappingProcessor.map(MappingProcessor.java:248)
at org.dozer.MappingProcessor.map(MappingProcessor.java:197)
at org.dozer.MappingProcessor.map(MappingProcessor.java:187)
at org.dozer.MappingProcessor.map(MappingProcessor.java:124)
at org.dozer.MappingProcessor.map(MappingProcessor.java:119)
at org.dozer.DozerBeanMapper.map(DozerBeanMapper.java:120)
at com.sg.rrf.l2r.server.audit.AuditTransactionalBean.getEntityForRevision(AuditTransactionalBean.java:30)
at com.sg.rrf.l2r.server.audit.AuditTransactionalBean$$FastClassByCGLIB$$78958945.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:713)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:98)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:262)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:52)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:52)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:58)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:646)
at com.sg.rrf.l2r.server.audit.AuditTransactionalBean$$EnhancerByCGLIB$$36312869.getEntityForRevision(<generated>)
at com.sg.rrf.l2r.server.audit.AuditServiceImpl.getEntityForRevision(AuditServiceImpl.java:37)
at com.sg.rrf.l2r.server.market.indicator.audit.IndicatorAuditServiceImplTest.assertElementaryValues(IndicatorAuditServiceImplTest.java:120)
at com.sg.rrf.l2r.server.market.indicator.audit.IndicatorAuditServiceImplTest.testAuditElementary(IndicatorAuditServiceImplTest.java:105)
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.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:232)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:89)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:175)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: java.lang.InstantiationException
at sun.reflect.InstantiationExceptionConstructorAccessorImpl.newInstance(InstantiationExceptionConstructorAccessorImpl.java:30)
at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
at org.dozer.factory.ConstructionStrategies$ByConstructor.newInstance(ConstructionStrategies.java:276)
... 74 more
Does it come from the fact that Envers use Lazy loading even when Eager is specified ?
PS : Of course, I need the bi-directional navigation from Indicator to Factor.
The field factors
was attempted to be mapped to a field factors
of a a new object of type Elementary, but the property here is a List interface for which no concrete type is known.
The mapping of this field works for actual domain model types, but not for hibernate proxies.
Do you initialize factors
with an ArrayList? It seems so otherwise the non Envers mapping would not work.
This could have to do with some limitations that dozer has in mapping generic types such as lists, due to the fact that generic information is not available at runtime, so dozer does not known the type of the objects that a list contains, so it tries to infer it from the contents of the source collection.
According to the Dozer documentation:
If a Hint is not specified for the destination field, then the destination Collection will be populated with objects that are the same type as the elements in the src Collection.
So to solve this there are several ways:
1 - Put a dozer hint on the mapping of this property to specify the target type, that way it will not try to infer it:
<field>
<a>factors</a>
<b>factors</b>
<b-hint>your.target.class.Here</b-hint>
</field>
2 - write and apply to this property a Dozer custom converter, where you map this list manually, this would always work (use the custom converter API that is generics based).
3 - Avoid the need for mapping and Dozer altogether, by solving the LazyInitialization at serialization time in another way: ensure the hibernate session is kept open all the way up to the serializing of the request, by using Open Session In View if in a Spring application, or similar if otherwise.
One of these ways should solve it, if still in doubts you can always:
post the dozer mapping and the code for the type of factors
?
With the debugger can you put a breakpoint in ConstructionStrategies line 280 to see what is the abstract class or interface that it's trying to instantiate.