首页 > 解决方案 > 每次 Spring MVC Controller 返回一个 ModelAndView Hibernate fire Selects

问题描述

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

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

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

例如

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

标签: springhibernatesessionmodel-view-controller

解决方案


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

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

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


推荐阅读