java如何正确处理多个并发事务请求的JPA ObjectOptimisticLockException?
所以,我在做一个简单的Spring MVC+JPA(hibernate)项目,在这个项目中,用户可以在朋友的帖子上发帖和发表评论(有点像一个小型社交网络)。我使用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?有什么好的模式我应该遵循吗?我需要使用某种悲观的锁定机制吗
这是我目前使用的刀。。提前感谢。:)
@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可以解决问题,但最终会出现相同的乐观锁定异常,因为加载时版本仍然低于数据库中当前的实体版本
此外,还可以使用悲观锁定(例如^{} 或^{}) ),这样,一旦获得了行级锁定,其他事务就不能修改锁定的记录