#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.