2

В моей модели домена у меня есть абстрактный объект Indicator, унаследованный двумя бетонными элементарными и составными.
Абстрактная сущность удерживает сущность Factor, чтобы быть доступной для двух подклассов.
Отношение двунаправлено. Таким образом, фактор сущности содержит экземпляр абстрактного объекта Indicator.
Конечно, реальный экземпляр является либо элементарным, либо композитным.Hibernate Envers: проблемы с полиморфизмом

@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 { 
    ... 
} 

Я использую Бульдозер для отображения этих объектов с собой для того, чтобы «сломать» в спящем режиме приборов и толкнуть их на стороне клиента (GWT).

Whith «classic» Hibernate, все отлично работает: бульдозер пересекает модель фасоли, чтобы ее дублировать.

Но, когда я использую Envers AuditReader для запроса версий, я получаю исключение InstantiationException. Это исходит из того, что экземпляр Factor попытаться создается экземпляр индикатора

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 

Исходит ли это из-за того, что Envers использовать отложенную загрузку даже если Нетерпеливый указано?

PS: Конечно, мне нужна двунаправленная навигация от индикатора к коэффициенту.

+0

У вас есть трассировку стека исключение или если оно недоступно, вы можете поместить контрольную точку в конструкторы исключения hibernate InstantiationException и использовать окно отладчика для копирования полного стека? –

+0

След стека добавлен – user3154016

ответ

0

В поле factors было показано, что поле factors представляет собой новый объект типа Элементарный, но здесь свойство представляет собой интерфейс List, для которого не известен конкретный тип.

Отображение этого поля работает для реальных типов моделей домена, но не для спящих прокси.

Вы инициализируете factors с помощью ArrayList? Кажется, что иначе отображение non Envers не будет работать.

Это может быть связано с некоторыми ограничениями, которые имеет у dozer при отображении общих типов, таких как списки, из-за того, что общая информация недоступна во время выполнения, поэтому бульдозер не знает тип объектов, которые содержатся в списке , поэтому он пытается вывести его из содержимого исходной коллекции.

Согласно Dozer documentation:

Если Совет не указан в поле назначения, то коллекция назначения будет заполняться с объектами, которые являются же типа, что и элементы в Src коллекции.

Так, чтобы решить это есть несколько способов:

1 - Поставьте бульдозерный намек на отображение этого свойства, чтобы определить тип цели, таким образом он не будет пытаться вывести его:

<field> 
    <a>factors</a> 
    <b>factors</b> 
    <b-hint>your.target.class.Here</b-hint> 
</field> 

2 - напишите и примените к этому свойству Dozer custom converter, где вы вручную сопоставляете этот список, это всегда будет работать (используйте API-интерфейс настраиваемого конвертера, основанный на генериках).

3 - Избегайте необходимости в картографировании и бульдозере вообще, решая LazyInitialization во время сериализации по-другому: убедитесь, что сеанс спящего режима поддерживается открытым вплоть до сериализации запроса, используя Open Session In View, если в Весеннее приложение или аналогичное, если в противном случае.

Один из этих способов должен решить его, если до сих пор в сомнениях вы всегда можете:

  • вывесить отображение бульдозерным и код для типа factors?

  • С помощью отладчика вы можете установить точку останова в строке ConstructionStrategies 280, чтобы узнать, что представляет собой абстрактный класс или интерфейс, которые он пытается создать.

+0

Также, если вы изучаете открытую сессию во избежание исключений из инициализации мазы, посмотрите на мое сообщение в блоге об этом http://blog.jhades.org/open-session-in-view-pattern- pros-and-cons/может помочь –

+0

Я заменил поддельное имя класса (сущность A, B, X, ...) с именами реальных классов: Indicator, Factor, ... – user3154016

0

Вот мой бульдозер отображение:

protected final static DozerBeanMapper MAPPER = new DozerBeanMapper(); 
static { 
    BeanMappingBuilder builder = new BeanMappingBuilder() { 
     @Override 
     protected void configure() { 
      mapping(Elementary.class, Elementary.class); 
      mapping(Composite.class, Composite.class); 
     } 
    }; 

    MAPPER.addMapping(builder); 
} 

Благодаря этому отображению, бульдозер может отображать Hibernate проксифицированной бобы.
Но с этим сопоставлением Dozer не может отображать Enders proxified beans.

Я не думаю, что проблема связана с картографией Collection. Когда я помещаю breakPoint в Instanciation, я использую метод org.dozer.factory.ConstructionStrategies.newInstance (Class clazz). Параметр clazz - Indicator.class.
Кроме того, предположим, что я не использую Бульдозер и я называю этот код в моем DAO:

Indicator indicator = elementary.getFactors().get(0).getIndicator(); 
System.out.println(indicator.getClass().getSimpleName()); 

Он печатает «Индикатор _ $$ _ javassist_6».

Испытание

indicator instanceof Elementary 

или

indicator instanceof Composite 

возвращение ложным.

Те же тесты с классической Hibernate (без Envers) возвращает истину для одного из них и напечатать правильное имя класса (даже с FetchType.LAZY на индикаторе ManyToOne атрибут!)

Смежные вопросы