【问题标题】:Every time Spring MVC Controller is returning a ModelAndView Hibernate fire Selects每次 Spring MVC Controller 返回一个 ModelAndView Hibernate fire Selects
【发布时间】:2019-03-19 05:31:43
【问题描述】:

我有一个基于 Spring MVC 和 Hibernate 的小型 Web 应用程序。我的 User 对象与 Pet 对象具有 oneToMany 关系:

@OneToMany(mappedBy="user", cascade=CascadeType.ALL, fetch=FetchType.EAGER)
private Set<Pet> myPets;

即使我在登录后将我的用户对象保留在 Session 中(我知道这是一种罪过),并且它包含了他的 Pets,每次调用 Controller 方法时,在它返回一个 ModelAndView 对象后,日志显示休眠触发选择。

例如

@RequestMapping(value = "/in_pet-see", method = RequestMethod.GET)
public ModelAndView getSee(@RequestParam("pid") Integer pId, ModelAndView model) {
    User u = getUserFromSession();
    if(u==null) {
        u = getUser();
    }
    Set<Pet> pets = u.getMyPets();
    Pet p = getPetFromSet(pets, pId);
    model.addObject("user", u);

    model.addObject("petType", petTypeMap.get(p.getType()));
    model.addObject("petAttribute", p);
    model.setViewName("b-see-pet");
    return model;
}

private User getUserFromSession() {
    return (User)session().getAttribute("user");
}

public static HttpSession session() {
    ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
    return attr.getRequest().getSession(true); // true == allow create
}


private Pet getPetFromSet(Set<Pet> set, int pId) {
    Pet myPet = null;
    for (Iterator<Pet> it = set.iterator(); it.hasNext(); ) {
        Pet p = it.next();
        if (p.getPetId()==pId) {
            myPet = p;
        }
        System.out.println("Pet found");

    }
    return myPet;
}

我怎样才能避免这种情况?

注意

我解决这个问题的方法是克隆休眠对象并将克隆放在 HttpSession 中,然后在那些可以对用户或用户的依赖对象进行更改(在我的应用程序中很少见)的方法中,在这些方法的末尾调用同步方法:

/** Get a new user from DDBB, after some change and put it on Session to deliver fresh data */
private void sinchronizeUser() {
    User oldSmith = getUserFromSession();
    User u = myUserService.loadUserById(oldSmith.getUserId());
    User smith = (User) u.clone();
    session().setAttribute("user", smith);
}

【问题讨论】:

  • 如果没有完整的控制器和流程,这是无法回答的。您还没有包含getPetFromSet。如果您有 @ModelAttribute 带注释的方法等,它也不可见。也不要使用该构造来获取 HttpSession 而只是将其作为 HttpSession 类型的方法参数注入。
  • 嗨,getPetFromSet 应该不重要。它只是集合内的一个迭代,寻找一个特定的 id
  • 您还没有添加完整的控制器,也没有解释发出了哪个选择语句。同样显然,如果没有用户,您可以执行其他操作来获取用户,这也让我想知道您如何将用户存储在会话中。总而言之,这里的图片不完整,无法回答您的问题。
  • 嗨,Deinum,谢谢,我的控制器很长,所有重要的东西都在这里。我终于找到了一种防止 Hibernate 选择的方法,它只是克隆 Hibernate 对象并在我的 HttpSession 中使用克隆对象。问候,
  • 这不是解决方案,而是破解/解决方法。 HttpSession 中的 User 不再附加到休眠状态,除非您将其存储在会话中的每个请求中,这基本上违背了将其存储在会话中的目的。

标签: spring hibernate session model-view-controller


【解决方案1】:

我猜这种行为是所谓的 Hibernate 脏检查机制的结果。在这里您可以了解更多信息:

https://vladmihalcea.com/the-anatomy-of-hibernate-dirty-checking/

就我而言,我终于找到了一种防止 Hibernate 选择的方法,它只是克隆 Hibernate 对象并在我的 HttpSession 中使用克隆对象。我的意思是我仍然使用 fetch eager,因为我需要预加载所有用户数据(很小)。并且使用克隆对象休眠不会触发任何更新。

【讨论】:

  • 它不应该这样做,因为当它在会话中时(假设你只存储一次而不是在每个请求上)它是分离的并且不再进行脏检查。
  • 是的,我不想每次都进行脏检查。这样我就可以控制它何时发生。
猜你喜欢
  • 1970-01-01
  • 2021-08-20
  • 1970-01-01
  • 1970-01-01
  • 2015-10-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-20
相关资源
最近更新 更多