2015-04-11 1 views
4

Итак, я работал над простым проектом Spring MVC + JPA (hibernate), где есть пользователи, которые могут создавать сообщения и делать комментарии к своим друзьям. Посты (несколько похожи на небольшую социальную сеть). Я по-прежнему относительно новый, используя JPA Hibernate. Поэтому, когда я пытаюсь проверить, что браузер отправляет несколько запросов для некоторой задачи (содержащей транзакции) очень быстро 2-3 раза, в то время как предыдущий запрос обрабатывается, я получаю исключение OptimisticLockException. Вот трассировка стека.Как правильно обрабатывать ObjectOptimisticLockException для нескольких одновременных запросов транзакций?

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); 
} 

}

+0

Вы видите сообщение об ошибке для пользователя, обновите страницу и попросите их повторить попытку, если они будут. Суть такого исключения состоит в том, чтобы сигнализировать о том, что сообщение изменилось или исчезло, и что модификация пользователя, возможно, уже не имеет смысла. Поэтому пользователь должен выбрать, что делать. –

ответ

9

Оптимистическая блокировка Исключение prevents lost updates и вы не должны игнорировать его. Вы можете просто поймать его в общем обработчике исключений и перенаправить пользователя в текущую начальную точку рабочего процесса, указывая на то, что он не знал об одновременном изменении.

  1. Если вы не против потери обновлений, вы можете удалить @Version аннотацию из ваших сущностей, поэтому loosing any optimistic locking data integrity guarantees.
  2. Вы можете auto-retry устаревший запрос на новый снимок базы данных сущностей.
  3. Кроме того, you can use pessimistic locking (e.g. PESSIMISTIC_WRITE or PESSIMISTIC_READ), так что, как только блокировка будет получена, никакая другая транзакция не сможет изменить заблокированные записи.
+0

большое спасибо. :) –