如何为多个并发事务请求正确处理 JPA ObjectOptimisticLockException?
Posted
技术标签:
【中文标题】如何为多个并发事务请求正确处理 JPA ObjectOptimisticLockException?【英文标题】:How to handle JPA ObjectOptimisticLockException properly for multiple simultaneous transaction requests? 【发布时间】:2015-06-16 23:09:49 【问题描述】:所以,我正在开发一个简单的 Spring MVC + JPA(休眠)项目,其中有用户可以发表帖子并对他们的朋友帖子发表评论(有点像一个小型社交网络)。我对使用 JPA Hibernate 还是比较陌生。因此,当我尝试从浏览器发送多个请求(包含事务)非常快速地测试 2-3 次同时处理先前的请求时,我得到一个 OptimisticLockException 。这是堆栈跟踪..
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.orm.ObjectOptimisticLockingFailureException: Object of class [org.facebookjpa.persistance.entity.Post] with identifier [19]: optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [org.facebookjpa.persistance.entity.Post#19]
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:973)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:852)
javax.servlet.http.HttpServlet.service(HttpServlet.java:620)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
现在,我该如何解决这个问题?当多个事务请求同时发生时,如何正确处理此 ObjectOptimisticLockException?我应该遵循什么好的模式吗?我需要使用某种悲观锁定机制吗?
这是我目前正在使用的 DAO .. 提前致谢。 :)
@Repository
@Transactional
public class PostDAOImpl implements PostDAO
@Autowired
UserDAO userDAO;
@Autowired
CommentDAO commentDAO;
@Autowired
LikeDAO likeDAO;
@PersistenceContext
private EntityManager entityManager;
public PostDAOImpl()
@Override
public boolean insertPost(Post post)
entityManager.persist(post);
return true;
@Override
public boolean updatePost(Post post)
entityManager.merge(post);
return true;
@Override
public Post getPost(int postId)
TypedQuery<Post> query = entityManager.createQuery("SELECT p FROM Post AS p WHERE p.id=:postId", Post.class);
query.setParameter("postId", postId);
return getSingleResultOrNull(query);
@Override
public List<Post> getAllPosts()
return entityManager.createQuery("SELECT p FROM Post AS p ORDER BY p.created DESC", Post.class).getResultList();
@Override
public List<Post> getNewsFeedPostsWithComments(int userId)
List<Post> newsFeedPosts = getUserPosts(userId);
newsFeedPosts.addAll(getFriendsPost(userDAO.getUser(userId)));
for (Post post : newsFeedPosts)
post.setComments(commentDAO.getPostComments(post.getId()));
post.setLikes(likeDAO.getPostLikes(post.getId()));
return newsFeedPosts;
public List<Post> getFriendsPost(User user)
List<Post> friendsPosts = new ArrayList<Post>();
for (User u : user.getFriends())
friendsPosts.addAll(getUserPosts(u.getId()));
return friendsPosts;
@Override
public List<Post> getUserPosts(int userId)
TypedQuery<Post> query = entityManager.createQuery("SELECT p FROM Post AS p WHERE p.user.id = :userId ORDER BY p.created DESC", Post.class);
query.setParameter("userId", userId);
return query.getResultList();
@Override
public List<Post> getUserPostsWithComments(int userId)
List<Post> userPostsWithComments = getUserPosts(userId);
for (Post post : userPostsWithComments)
post.setComments(commentDAO.getPostComments(post.getId()));
post.setLikes(likeDAO.getPostLikes(post.getId()));
return userPostsWithComments;
@Override
public boolean removePost(Post post)
entityManager.remove(post);
return true;
@Override
public boolean removePost(int postId)
entityManager.remove(getPost(postId));
return true;
private Post getSingleResultOrNull(TypedQuery<Post> query)
query.setMaxResults(1);
List<Post> list = query.getResultList();
if (list.isEmpty())
return null;
return list.get(0);
【问题讨论】:
您向用户显示一条错误消息,刷新页面并要求他们再试一次。这种异常的意义在于表明帖子已更改或消失,并且用户修改可能不再有意义。所以用户应该选择做什么。 【参考方案1】:JPA OptimisticLockException
阻止了lost updates,你不应该忽略它。
您可以简单地在通用异常处理程序中捕获它,并将用户重定向到当前正在执行的工作流的起点,这表明该流必须重新启动,因为它使用的是陈旧数据。
但是,如果您的应用程序要求不需要防止丢失更新异常,那么您可以简单地从您的实体中删除 @Version
注释,从而破坏可序列化性。
现在,您可能认为针对新实体数据库快照的 auto-retry 可以解决问题,但您最终会遇到相同的乐观锁定异常,因为加载时版本仍然低于当前实体版本数据库。
此外,您可以使用悲观锁定(例如PESSIMISTIC_WRITE
或PESSIMISTIC_READ
),这样一旦获得行级锁定,其他事务就不能修改锁定的记录。
【讨论】:
以上是关于如何为多个并发事务请求正确处理 JPA ObjectOptimisticLockException?的主要内容,如果未能解决你的问题,请参考以下文章
你如何为 Seam / JPA (hibernate) 创建一个 DAO 类?
如何为 JPA 和 Hibernate 创建一个 persistence.xml 文件?
Java NIO :如何为通道注册多个事件及多线程处理 Accetp 请求