java - 升级后,@MapsId 在保存现有实体时抛出错误,但在其他方面工作正常

标签 java hibernate spring-boot jpa one-to-one

我正在努力将 Spring Boot 1.5.21 项目 (Java 8u221) 升级到 Spring Boot 2.1.9 (Java 11.0.2-open)。在这两种情况下,我们都使用带有 spring boot 启动器和依赖项解析器的 gradle 构建,因此底层 Spring、JPA 和 Hibernate 库的版本是 spring 管理的。

该项目有一个可选的一对一映射,其中子实体从父实体生成的 ID 中获取其 ID。在Spring Boot 1版本的项目中,关系是这样配置的:

@Entity
@Table(name = "PARENT_OBJECT", schema = "MYSCHEMA")
public class ParentObject implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "PARENT_OBJECT_ID_SEQ")
    @SequenceGenerator(name = "PARENT_OBJECT_ID_SEQ", sequenceName = "MYSCHEMA.PARENT_OBJECT_ID_SEQ")
    protected Long id;

    @OneToOne(optional = true, mappedBy = "parentObject", cascade = CascadeType.ALL, orphanRemoval = true)
    @Valid
    protected ChildObject childObject;

    // Other fields and methods
}

@Entity
@Table(name = "CHILD_OBJECT", schema = "MYSCHEMA")
public class ChildObject implements Serializable {
    @Id
    @Column(name = "parent_object_id", unique = true, nullable = false, insertable = true, updatable = false)
    private Long parentObjectId;

    @OneToOne
    @MapsId
    @PrimaryKeyJoinColumn
    @JsonIgnore
    private ParentObject parentObject;

    // Other fields and methods
}

当我更新到 Spring Boot 2 时,我不得不更新很多 JPA 设置,特别是被提示的一件事是生成的查询正在寻找“PARENTOBJECT_ID”而不是'PARENT_OBJECT_ID',所以我仔细研究了一下,发现一篇文章解释了如何使用 @JoinColumn 修复一对一映射的列名,并将 parentObject 字段的子对象注释更新为如下所示:

@OneToOne
@MapsId
@JoinColumn(name = "parent_object_id")
@JsonIgnore
private ParentObject parentObject;

我完全删除了 @PrimaryKeyJoinColumn,因为文档似乎建议您应该使用 @MapsId 如果您希望 Hibernate 处理 ID 的分配,并且如果您如果您要自己管理它们,那就是使用 @PrimaryKeyJoinColumn 的时候。

此配置适用于我的所有测试,除了 2: Controller 的 UPDATE 集成测试(POST 测试工作得很好,当第一次创建 ParentObject 及其关联的 ChildObject 时)。 我在这两个测试中得到的错误是:

org.springframework.orm.jpa.JpaSystemException: attempted to assign id from null one-to-one property [org.mycompany.myproject.mymodule.mysubmodule.ChildObject.parentObject]; nested exception is org.hibernate.id.IdentifierGenerationException: attempted to assign id from null one-to-one property [org.mycompany.myproject.mymodule.mysubmodule.ChildObject.parentObject]

奇怪的是,这些实体及其关联的所有存储库集成测试都通过了,以及所有其他 Controller 集成测试,包括一个新的 ParentObject 被发布到它的新的测试子对象。我四处寻找可能的解决方案,但我阅读的每篇文章似乎都表明我使用的配置应该有效。我还尝试了建议的替代配置,例如使用 @PrimaryKeyJoinColumn 和自己设置 ID 字段,在 ChildObject 的 ID 上使用 ID 生成器(即使 @MapsId 是告诉系统使用 ParentObject 的 ID),通常我最终会遇到更多失败的测试,并出现以下错误:

org.springframework.orm.jpa.JpaSystemException: ids for this class must be manually assigned before calling save(): org.mycompany.myproject.mymodule.mysubmodule.ChildObject; nested exception is org.hibernate.id.IdentifierGenerationException: ids for this class must be manually assigned before calling save(): org.mycompany.myproject.mymodule.mysubmodule.ChildObject

虽然有时我会得到无法“从 null 分配 id”的原始错误。在这一点上,我不知道这是如何配置不正确的,或者还有什么其他因素在搞砸。此时我愿意尝试所有建议。

为了完整起见,以下是应用程序中的一堆代码片段。 堆栈跟踪中的相关行号用注释注明。我省略了 Controller 代码,因为它没有什么特别之处; POST 和 PUT 方法调用相同的服务方法;它们只是位于不同的端点,PUT 在调用服务保存方法之前首先检查对象是否存在于数据库中。下面是为 ParentObject 调用 JPA 存储库的服务方法:

@Service
@Transactional(readOnly = true)
public class ParentObjectServiceImpl implements ParentObjectService {
    // other fields and other methods

    @Autowired
    private ParentObjectRepository parentObjectRepository;

    @Transactional(readOnly = false)
    @Override
    public ParentObject saveParentObject(final ParentObject parentObject) {
        parentObject.prepForPersistence();
        return parentObjectRepository.save(parentObject); //Line 93
    }
}

这是我的 JPA 存储库:

public interface ParentObjectRepository extends CrudRepository<ParentObject, Long> {
    // custom methods, no override for save though
}

下面是 ParentObjectprepForPersistence() 方法:

public void prepForPersistence() {
    if(childObject != null) {
        childObject.setParentObject(this);
        // In some iterations of the code in trying to solve this, I also had the following line
        //childObject.setParentObjectId(this.id);
    }
}

这里有两个测试(一个通过,一个失败):

@RunWith(SpringRunner.class)
@SpringBootTest(classes = MyApplication.class)
@WebAppConfiguration
// This profile disabled csrf for testing, and sets some env variables
@ActiveProfiles("integration-test")
@Transactional
public class ParentObjectControllerTest {
    @Autowired
    private WebApplicationContext context;

    private MockMvc mockMvc;

    @Before
    public void setUp() throws Exception {
        mockMvc = MockMvcBuilders.webAppContextSetup(context)
            .apply(springSecurity())
            .build();
    } 

    // This test PASSES
    @Test
    @WithMockUser(roles = {"MY_APP_ADMIN"}, username = TEST_USER)
    @Sql(scripts = "/db-scripts/bootstrap.sql")
    public void testPostParentObjectWithChildObject() {
        final ChildObject childObject = new ChildObject();
        // set some properties on childObject that don't relate to ParentObject

        final ParentObject parentObject = new ParentObject();
        // set some properties on parentObject that don't relate to ChildObject
        parentObject.setChildObject(childObject);

        final ParentObject result = given().mockMvc(mockMvc).contentType(ContentType.JSON)
            .and().body(item).log().all()
            .when().post("/parent-objects")
            .then().log().all().statusCode(201).contentType(ContentType.JSON)
            .and().body(matchesJsonSchemaInClasspath("json-schemas/parent-object.json"))
            .and().body("username", equalTo(TEST_USER))
            .extract().as(ParentObject.class);

        assertThat(result.getId(), is(notNullValue()));
        assertThat(result.getChildObject().getParentObjectId(), is(result.getId()));
    }

    // This test FAILS with the 'attempted to assign id from null one-to-one property' error
    @Test
    @WithMockUser(roles = {"MY_APP_ADMIN"}, username = TEST_USER)
    // Inserts a ParentObject record with ID -1
    @Sql(scripts = "/db-scripts/bootstrap.sql")
    public void testPutParentObjectWithChildObject() {
        final ChildObject childObject = new ChildObject();
        // set some properties on childObject that don't relate to ParentObject

        final Long EXISTING_ID = -1L;
        final ParentObject parentObject = new ParentObject();
        parentObject.setId(EXISTING_ID);
        parentObject.setChildObject(childObject);

        final ParentObject result = given().mockMvc(mockMvc).contentType(ContentType.JSON)
            .and().body(item).log().all()
            .when().put("/parent-objects/{parentObjectId}", parentObject.getId()) //line 260
            .then().log().all().statusCode(200)
            .extract().as(ParentObject.class);

        assertThat(result.getId(), is(EXISTING_ID));
        assertThat(result.getChildObject().getParentObjectId(), is(EXISTING_ID));
}

这是完整的堆栈跟踪:

Responding with HTTPStatus=INTERNAL_SERVER_ERROR due to the following error: org.springframework.orm.jpa.JpaSystemException: attempted to assign id from null one-to-one property [org.mycompany.myproject.mymodule.mysubmodule.ChildObject.parentObject]; nested exception is org.hibernate.id.IdentifierGenerationException: attempted to assign id from null one-to-one property [org.mycompany.myproject.mymodule.mysubmodule.ChildObject.parentObject] at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:352) at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:254) at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:528) at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61) at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242) at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:153) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:144) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$ExposeRepositoryInvocationInterceptor.invoke(CrudMethodMetadataPostProcessor.java:364) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:61) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) at com.sun.proxy.$Proxy249.save(Unknown Source) at org.mycompany.myproject.mymodule.ParentObjectServiceImpl.saveParentObject(ParentObjectServiceImpl.java:93) at org.mycompany.myproject.mymodule.ParentObjectServiceImpl$$FastClassBySpringCGLIB$$86531367.invoke() at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:750) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:295) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689) at org.mycompany.myproject.mymodule.ParentObjectServiceImpl$$EnhancerBySpringCGLIB$$d3a0c3ee.saveChecklistItem() at org.mycompany.myproject.mymodule.ParentObjectController.putParentObject(ParentObjectController.java:78) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:566) at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190) at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:105) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:893) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:798) at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) at org.springframework.web.servlet.FrameworkServlet.doPut(FrameworkServlet.java:920) at javax.servlet.http.HttpServlet.service(HttpServlet.java:668) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) at org.springframework.test.web.servlet.TestDispatcherServlet.service(TestDispatcherServlet.java:72) at javax.servlet.http.HttpServlet.service(HttpServlet.java:750) at org.springframework.mock.web.MockFilterChain$ServletFilterProxy.doFilter(MockFilterChain.java:167) at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:320) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127) at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:97) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:74) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215) at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178) at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134) at org.springframework.test.web.servlet.MockMvc.perform(MockMvc.java:183) at io.restassured.module.mockmvc.internal.MockMvcRequestSenderImpl.performRequest(MockMvcRequestSenderImpl.java:218) at io.restassured.module.mockmvc.internal.MockMvcRequestSenderImpl.sendRequest(MockMvcRequestSenderImpl.java:447) at io.restassured.module.mockmvc.internal.MockMvcRequestSenderImpl.put(MockMvcRequestSenderImpl.java:504) at io.restassured.module.mockmvc.internal.MockMvcRequestSenderImpl.put(MockMvcRequestSenderImpl.java:100) at org.mycompany.myproject.mymodule.ParentObjectControllerTest.testPutParentObjectWithChildObject(ParentObjectControllerTest.java:260) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:566) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74) at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84) at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26) at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75) at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86) at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190) at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:110) at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58) at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:38) at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:62) at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:566) at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36) at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33) at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94) at com.sun.proxy.$Proxy5.processTestClass(Unknown Source) at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:118) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:566) at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36) at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:182) at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:164) at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:412) at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64) at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56) at java.base/java.lang.Thread.run(Thread.java:834) Caused by: org.hibernate.id.IdentifierGenerationException: attempted to assign id from null one-to-one property [org.mycompany.myproject.mymodule.mysubmodule.ChildObject.parentObject] at org.hibernate.id.ForeignGenerator.generate(ForeignGenerator.java:90) at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:119) at org.hibernate.event.internal.DefaultMergeEventListener.saveTransientEntity(DefaultMergeEventListener.java:287) at org.hibernate.event.internal.DefaultMergeEventListener.entityIsTransient(DefaultMergeEventListener.java:259) at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:191) at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:927) at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:897) at org.hibernate.engine.spi.CascadingActions$6.cascade(CascadingActions.java:261) at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:490) at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:415) at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:216) at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:149) at org.hibernate.event.internal.DefaultMergeEventListener.cascadeOnMerge(DefaultMergeEventListener.java:532) at org.hibernate.event.internal.DefaultMergeEventListener.entityIsDetached(DefaultMergeEventListener.java:361) at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:188) at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:72) at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:905) at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:891) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:566) at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:310) at com.sun.proxy.$Proxy214.merge(Unknown Source) at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:538) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:566) at org.springframework.data.repository.core.support.RepositoryComposition$RepositoryFragments.invoke(RepositoryComposition.java:359) at org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:200) at org.springframework.data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke(RepositoryFactorySupport.java:644) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:608) at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.lambda$invoke$3(RepositoryFactorySupport.java:595) at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:595) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:59) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:295) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139) ... 140 more

在此先感谢您的建议和帮助!

最佳答案

我认为这是一个错误,但如果有人有解决方法,我仍然对解决方法感兴趣。我最初在 hibernate 上发现了一个似乎与问题匹配的错误,但它被注册为仅影响 5.2 系列并标记为“已修复”,由于我们的项目使用的是 5.3 系列,我继续前进。尽管从 spring 方面深入研究提交的错误,面包屑引导我找到了这个 5.3 系列错误:https://hibernate.atlassian.net/browse/HHH-13413这与我最初发现的 5.2 系列错误相关联,与 5.3 的文章相反,这似乎表明自 5.2 系列以来这一直是一个问题,并且在 5.3 系列中应该从来没有工作过,因为,不仅5.3 系列错误是否被标记为 5.2 系列错误的副本,当我仔细查看 5.2 错误时,它表示它的修复版本直到 5.4:https://hibernate.atlassian.net/browse/HHH-12436

所以我将尝试降级到最后一个有效的 5.2 系列,并升级到 5.4 系列......我只是不确定它们中的任何一个将如何与其余的 spring 数据 jpa 功能一起使用,并且可能本身就是一个大问题。

更新:除了因为我们使用 java 11 而遇到问题(https://hibernate.atlassian.net/browse/HHH-12924 -> 我不得不使用 javassist:3.23.0-GA 来让 hibernate-core:5.2.13.Final 工作),降级成功地使一对一映射再次工作。所有其他测试也都通过了。但是,我没有对该解决方案进行广泛的测试,因为我更喜欢基于升级的解决方案。

更新 2:能够升级到 Spring Boot 2.2.0(使用 Hibernate 5.4,修复了一对一映射的这个特定错误)!它需要对我的序列进行一些更改,但升级并不太痛苦;甚至不需要使用降级标志!如果碰巧您的映射问题没有得到解决,则有一个“使用 5.2 行为”标志,因为他们仍在解决各种映射场景的问题。有关其 5.4 迁移说明的更多详细信息,请参见此处:https://github.com/hibernate/hibernate-orm/blob/61cddad76d5bba951805fa7ed90cc149d404841c/migration-guide.adoc

关于java - 升级后,@MapsId 在保存现有实体时抛出错误,但在其他方面工作正常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58561156/

相关文章:

java - 从数据表中检索输入

java - 为什么我在运行一个简单的 Spring Boot 应用程序时总是得到状态为 "404"的 Whitelabel 错误页面

java - Spring Boot 与 Groovy 模板——无法迭代 ModelAndView 中的列表

java - HQL 中的内部连接查询

java - Hibernate 5 中的 StatelessSessionImpl

java - Hibernate - 映射同一列的两个字段的不同值

java - Android proguard,保留内部类的内部类

java泛型问题

java - 如何在Spring React的ServerHttpResponse中设置非标准的HttpStatus?

java - Spring 中的负载平衡 LDAP 连接