You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java中Object Optimistic Locking Failure Exception异常排查求助

Object Optimistic Locking Failure Exception异常排查与解决

问题描述

我遇到了ObjectOptimisticLockingFailureException异常。系统模型包含Post、Users、Comment、Message、Email和Status;PostService实现了帖子的创建、编辑、删除等逻辑,UserService中会调用PostService,通过ID查询帖子、校验用户后,将帖子添加到用户的帖子列表中。运行测试时抛出该异常。已尝试添加@Transactional管理事务,且在Post模型中添加了@Version注解,但问题仍未解决。

测试代码

@Test
public void testThatUsersCanUploadPost() throws UserAlreadyExistException, UserNotFoundException, PostNotFoundException {
    buildUsersRegistrationRequest();
    RegisterUsersResponse registerUserResponse = userService.register(buildUsersRegistrationRequest());
    assertNotNull(registerUserResponse);
    CreatePostRequest createPostRequest = new CreatePostRequest();
    createPostRequest.setReferencePostId(1L);
    createPostRequest.setUserId(1L);
    createPostRequest.setContent("Software engineering no be garri!!");
    createPostRequest.setImageUrl("http://localhost");
    createPostRequest.setPostCreatedAt(now());
    CreatePostResponse response = postService.createPost(createPostRequest);
    assertEquals("Post created successfully", response.getMessage());
    CreatePostResponse userCreatedPostResponse = userService.createContent(createPostRequest);
    assertEquals("Post created successfully", userCreatedPostResponse.getMessage());
    assertNotNull(userCreatedPostResponse);
    log.info("userCreatedPostResponse: {}", userCreatedPostResponse);
    log.info("response: {}", response);
}

实现代码

@Override
public CreatePostResponse createContent(CreatePostRequest createPostRequest) throws UserNotFoundException, PostNotFoundException {
   CreatePostResponse createPostResponse = postService.createPost(createPostRequest);
  Post post = postService.getPostsById(createPostRequest.getReferencePostId());
   Users user = userRepository.findById(createPostRequest.getUserId())
           .orElseThrow(() -> new UserNotFoundException(String.format("User %s not found", createPostRequest)));
   List<Post> listOfPost = user.getPosts();
   listOfPost.add(post);
   userRepository.save(user);
   createPostResponse.setMessage("Content created successfully");
    return createPostResponse;
}

堆栈信息

org.springframework.orm.ObjectOptimisticLockingFailureException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.fontmedia.africa.nija.fontmedia.data.model.Post#1]

    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:325)
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:244)
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:560)
    at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61)
    at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:343)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:160)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
    at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:165)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:223)
    at jdk.proxy2/jdk.proxy2.$Proxy116.save(Unknown Source)
    at com.fontmedia.africa.nija.fontmedia.service.implimentation.PostServiceImpl.createPost(PostServiceImpl.java:32)
    at com.fontmedia.africa.nija.fontmedia.service.interfaces.UserServiceTest.testThatUsersCanUploadPost(UserServiceTest.java:66)
    at java.base/java.lang.reflect.Method.invoke(Method.java:580)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
Caused by: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.fontmedia.africa.nija.fontmedia.data.model.Post#1]
    at org.hibernate.event.internal.DefaultMergeEventListener.entityIsDetached(DefaultMergeEventListener.java:426)
    at org.hibernate.event.internal.DefaultMergeEventListener.merge(DefaultMergeEventListener.java:214)
    at org.hibernate.event.internal.DefaultMergeEventListener.doMerge(DefaultMergeEventListener.java:152)
    at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:136)
    at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:89)
    at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:127)
    at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:854)
    at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:840)
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
    at java.base/java.lang.reflect.Method.invoke(Method.java:580)
    at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:320)
    at jdk.proxy2/jdk.proxy2.$Proxy112.merge(Unknown Source)
    at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:626)
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
    at java.base/java.lang.reflect.Method.invoke(Method.java:580)
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:359)
    at org.springframework.data.repository.core.support.RepositoryMethodInvoker$RepositoryFragmentMethodInvoker.lambda$new$0(RepositoryMethodInvoker.java:277)
    at org.springframework.data.repository.core.support.RepositoryMethodInvoker.doInvoke(RepositoryMethodInvoker.java:170)
    at org.springframework.data.repository.core.support.RepositoryMethodInvoker.invoke(RepositoryMethodInvoker.java:158)
    at org.springframework.data.repository.core.support.RepositoryComposition$RepositoryFragments.invoke(RepositoryComposition.java:515)
    at org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:284)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke(RepositoryFactorySupport.java:752)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
    at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:174)
    at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:149)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
    at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:69)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:380)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:138)
    ... 10 more

2024-12-01T12:45:32.648+01:00  INFO 12584 --- [fontmedia] [ionShutdownHook] j.LocalCo

问题分析与解决方案

核心原因

  1. 测试代码重复调用帖子创建逻辑:先调用postService.createPost,又在userService.createContent里再次调用该方法,两次操作修改同一Post记录(ID=1),触发乐观锁版本冲突。
  2. createContent方法中,重新查询Post后添加到User列表,执行userRepository.save(user)时,Hibernate尝试合并已被其他事务修改的Post实体,版本号不匹配引发异常。

修复步骤

  1. 移除测试中的重复创建操作:仅调用userService.createContent即可,内部会处理帖子创建和关联逻辑:
@Test
public void testThatUsersCanUploadPost() throws UserAlreadyExistException, UserNotFoundException {
    buildUsersRegistrationRequest();
    RegisterUsersResponse registerUserResponse = userService.register(buildUsersRegistrationRequest());
    assertNotNull(registerUserResponse);
    CreatePostRequest createPostRequest = new CreatePostRequest();
    createPostRequest.setUserId(1L);
    createPostRequest.setContent("Software engineering no be garri!!");
    createPostRequest.setImageUrl("http://localhost");
    createPostRequest.setPostCreatedAt(now());
    CreatePostResponse userCreatedPostResponse = userService.createContent(createPostRequest);
    assertEquals("Content created successfully", userCreatedPostResponse.getMessage());
    assertNotNull(userCreatedPostResponse);
    log.info("userCreatedPostResponse: {}", userCreatedPostResponse);
}
  1. 优化createContent方法逻辑:直接使用createPost返回的Post对象,避免重新查询导致的分离实体问题:
@Override
public CreatePostResponse createContent(CreatePostRequest createPostRequest) throws UserNotFoundException {
   CreatePostResponse createPostResponse = postService.createPost(createPostRequest);
   // 直接使用创建返回的Post对象,无需二次查询
   Post post = createPostResponse.getPost();
   Users user = userRepository.findById(createPostRequest.getUserId())
           .orElseThrow(() -> new UserNotFoundException(String.format("User %s not found", createPostRequest.getUserId())));
   user.getPosts().add(post);
   userRepository.save(user);
   createPostResponse.setMessage("Content created successfully");
    return createPostResponse;
}
  1. 调整事务传播行为:为createContent添加@Transactional(propagation = Propagation.REQUIRED),确保帖子创建和用户关联操作在同一事务内执行,避免多事务修改同一实体。

内容的提问来源于Stack Exchange,提问作者Mfon Mfon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 20:42:32