Как запросить порядок каскадных операций с помощью Hibernate / JPA?

#java #hibernate #jpa

#java #переход в спящий режим #jpa

Вопрос:

В последнее время я с успехом использую JPA / Hibernate, но у меня все еще возникают проблемы с поиском ошибок в моих объектах, особенно когда ошибка возникает из-за каскадной операции сохранения.

Мы используем довольно сложный дизайн базы данных со многими отношениями, и мой обычный постоянный подход заключается в настройке набора объектов, назначении всех связей между ними и последующем вызове persist() корневого объекта.

Иногда это приводит к SELECT ошибке, в которой Hibernate жалуется, что не смог найти объект с заданным идентификатором. Это несмотря на то, что Hibernate должен был в INSERT первую очередь относиться к этому объекту.

Чаще всего это проблема каскадирования, и ее можно было бы легко отладить, если бы у меня был способ спросить Hibernate, какие операции он намерен выполнить (и в каком порядке) для достижения persist() того, о чем я спрашиваю.

Как я могу получить обратную связь для стратегии сохранения гибернации?

Я знаю, как запрашивать журналы SQL, но они обычно очень неясные и подробные и совсем не объясняют, почему указаны некоторые команды SQL.


В качестве практического примера, этим утром я немного застрял на такого рода исключениях:

 org.springframework.orm.jpa.JpaObjectRetrievalFailureException: Unable to find foo.bar.product.model.entity.FooEntity with id 113381; nested exception is javax.persistence.EntityNotFoundException: Unable to find foo.bar.product.model.entity.FooEntity with id 113381
    at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:389)
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:246)
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:491)
    at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:59)
    at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:147)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:133)
    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.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
    at com.sun.proxy.$Proxy88.save(Unknown Source)
    at foo.bar.product.model.service.WorkloadService.updateAndSaveAllDependeceWithNewWklId(WorkloadService.java:154)
    at foo.bar.product.model.service.WorkloadService$$FastClassBySpringCGLIB$$9cf9789e.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:720)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:280)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:655)
    at foo.bar.product.model.service.WorkloadService$$EnhancerBySpringCGLIB$$1e47513d.updateAndSaveAllDependeceWithNewWklId(<generated>)
    at foo.bar.product.FooFunction.run(FooFunction.java:137)
    at foo.bar.product.FunctionSelectorMain.run(FunctionSelectorMain.java:47)
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:789)
    at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:779)
    at org.springframework.boot.SpringApplication.afterRefresh(SpringApplication.java:769)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:314)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1185)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1174)
    at foo.bar.product.FunctionSelectorMain.main(FunctionSelectorMain.java:29)
Caused by: javax.persistence.EntityNotFoundException: Unable to find foo.bar.product.model.entity.FooEntity with id 113381
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$JpaEntityNotFoundDelegate.handleEntityNotFound(EntityManagerFactoryBuilderImpl.java:144)
    at org.hibernate.event.internal.DefaultLoadEventListener.load(DefaultLoadEventListener.java:227)
    at org.hibernate.event.internal.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:278)
    at org.hibernate.event.internal.DefaultLoadEventListener.doOnLoad(DefaultLoadEventListener.java:121)
    at org.hibernate.event.internal.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:89)
    at org.hibernate.internal.SessionImpl.fireLoad(SessionImpl.java:1129)
    at org.hibernate.internal.SessionImpl.internalLoad(SessionImpl.java:1022)
    at org.hibernate.type.EntityType.resolveIdentifier(EntityType.java:632)
    at org.hibernate.type.EntityType.resolve(EntityType.java:424)
    at org.hibernate.type.EntityType.replace(EntityType.java:323)
    at org.hibernate.type.CollectionType.replaceElements(CollectionType.java:518)
    at org.hibernate.type.CollectionType.replace(CollectionType.java:663)
    at org.hibernate.type.AbstractType.replace(AbstractType.java:147)
    at org.hibernate.type.TypeHelper.replaceAssociations(TypeHelper.java:261)
    at org.hibernate.event.internal.DefaultMergeEventListener.copyValues(DefaultMergeEventListener.java:427)
    at org.hibernate.event.internal.DefaultMergeEventListener.entityIsTransient(DefaultMergeEventListener.java:240)
    at org.hibernate.event.internal.DefaultMergeEventListener.entityIsDetached(DefaultMergeEventListener.java:301)
    at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:170)
    at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:69)
    at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:840)
    at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:822)
    at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:827)
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.merge(AbstractEntityManagerImpl.java:1161)
    at sun.reflect.GeneratedMethodAccessor45.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:298)
    at com.sun.proxy.$Proxy82.merge(Unknown Source)
    at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:509)
    at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:540)
    at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:72)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:503)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:488)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:460)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:280)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
    ... 27 more
  

и проблема здесь просто заключалась в отсутствующем каскаде сохранения в одной из сущностей, связанных с fooEntity , но найти, какая из них была болезненной без надлежащего вывода в режиме гибернации.

Комментарии:

1. Не могли бы вы, пожалуйста, опубликовать исключение, которое вы получаете?

2. Я бы рекомендовал проанализировать, содержит ли ваш объектный граф циклы. Это может быть причиной вашей проблемы.

3. Я добавил пример, но мой вопрос не об этой конкретной отладке, потому что со временем я обычно добираюсь до конца ошибок. Вопрос заключается в том, как получить надлежащие журналы из Hibernate для расследования такого рода проблем.

Ответ №1:

Насколько я знаю, вы застряли с журналами SQL.

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

Вы, конечно, можете иметь разумное представление о порядке сохранения, посмотрев на модель предметной области и транзакции, но для сложных операций это становится головной болью.

Ответ №2:

В конце концов я нашел класс Hibernate, отвечающий за каскадные операции JPA.

В общем случае достаточно увеличить уровень журнала для org.hibernate.engine.internal.Cascade до TRACE , и появятся полезные сообщения.

В сообщении будет объяснено для каждого объекта, какие другие объекты будут подвержены операциям из-за определений каскада.

Например, в моем файле Spring Boot application.properties я написал:

 logging.level.org.hibernate.engine.internal.Cascade=TRACE
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type=TRACE
  

для правильной отладки каскадных операций и их операторов SQL.