java 如何为多个并发事务请求正确处理 JPA ObjectOptimisticLockException?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/29574859/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 15:31:11  来源:igfitidea点击:

How to handle JPA ObjectOptimisticLockException properly for multiple simultaneous transaction requests?

javahibernatejpatransactionsoptimistic-locking

提问by adn.911

So, I was working on a simple Spring MVC + JPA (hibernate) project where there are Users who can makes Posts and make Comments on their friends Posts (somewhat like a small social network) . I am still relatively new using JPA Hibernate. So, when I try to test from browser sending multiple requests for some task ( containing transactions) very quickly 2-3 times while a previous request is being processed I get an OptimisticLockException . Here 's the stack trace ..

所以,我正在做一个简单的 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)

Now, how do i fix this? How do i handle this ObjectOptimisticLockException properly when multiple transaction requests occurs simultaneously ? Is there any good patten that i should follow ? Do i need to use some sort of Pessimistic Locking mechanism ?

现在,我该如何解决这个问题?当多个事务请求同时发生时,我如何正确处理这个 ObjectOptimisticLockException ?有什么好的模式我应该遵循吗?我需要使用某种悲观锁定机制吗?

Here's the DAO that i am currently using .. Thanks in advance . :)

这是我目前正在使用的 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);
}

}

}

回答by Vlad Mihalcea

The Optimistic Locking Exception prevents lost updates, and you shouldn't ignore it.

Optimistic Locking Exception防止丢失更新,您不应该忽略它。

You can simply catch it in a common exception handler and redirect the user to the current workflow starting point, indicating there was a concurrent change that he was not aware of.

您可以简单地在公共异常处理程序中捕获它并将用户重定向到当前工作流起点,表明存在他不知道的并发更改。

If you don't mind loosing updates, you can remove the @Versionannotation from your entities, therefore loosing any optimistic locking data integrity guarantees.

如果您不介意丢失更新,则可以@Version从实体中删除注释,从而丢失任何乐观锁定数据完整性保证

Now, you might think that an auto-retryagainst a fresh entity database snapshot will fix the problem, but you will end up with the same optimistic locking exception since the load-time version is still lower than the current entity version in the DB.

现在,您可能认为针对新实体数据库快照的自动重试将解决该问题,但您最终会遇到相同的乐观锁定异常,因为加载时版本仍然低于数据库中的当前实体版本。

Also, you can use pessimistic locking (e.g. PESSIMISTIC_WRITEor PESSIMISTIC_READ)so that, once a row-level lock is acquired, no other transaction can modify the locked record.

此外,您可以使用悲观锁定(例如PESSIMISTIC_WRITEPESSIMISTIC_READ),以便一旦获得行级锁定,其他事务就无法修改锁定的记录。