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
问题分析与解决方案
核心原因
- 测试代码重复调用帖子创建逻辑:先调用
postService.createPost,又在userService.createContent里再次调用该方法,两次操作修改同一Post记录(ID=1),触发乐观锁版本冲突。 createContent方法中,重新查询Post后添加到User列表,执行userRepository.save(user)时,Hibernate尝试合并已被其他事务修改的Post实体,版本号不匹配引发异常。
修复步骤
- 移除测试中的重复创建操作:仅调用
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); }
- 优化
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; }
- 调整事务传播行为:为
createContent添加@Transactional(propagation = Propagation.REQUIRED),确保帖子创建和用户关联操作在同一事务内执行,避免多事务修改同一实体。
内容的提问来源于Stack Exchange,提问作者Mfon Mfon
相关产品推荐
相关产品推荐

