在Hibernate + Spring上更新实体的正确方法

时间:2012-04-18 13:38:23

标签: spring hibernate caching merge

我有这个问题:

我用实体准备视图

@RequestMapping("/admin/modifyImplant/{id}")
 public ModelAndView modifyImplant(@PathVariable Integer id) {
  ModelAndView mav = new ModelAndView();

  Implant currentImplant =impDAO.findById(id); //this is the dao
  Implanttype it= currentImplant.getImplanttype();
  Implantinverter ii=new Implantinverter();
  ArrayList<Implantpanel> implantpanels = new ArrayList<Implantpanel>();
  try{
      ii= (Implantinverter)currentImplant.getImplantinverters().toArray()[0];
      implantpanels=(ArrayList<Implantpanel>) imppanelDAO.findByProperty("implant",currentImplant );
  }
  catch (Exception ex){
      //TODO
  }
  mav.addObject("implantpanels", implantpanels);
  mav.addObject("implantinverter",ii);
  mav.addObject("implant", currentImplant); //THIS IS THE ENTITY I MEAN
  mav.addObject("implanttype",it);
  mav.setViewName("/implant/modifyImplant.jsp");

  return mav;


}

这个代码接近jsp(代码的一小部分)

<form:form action="${pageContext.request.contextPath}/admin/finalizeModifyImplant/${implant.id}" method="POST" modelAttribute="implant">
        <table cellpadding="0" cellspacing="0" id="viewTable" >
            <tbody>
                <tr>
                <td class="label">
                    Name:
                </td>   
                <td>    
                    <form:input  id="User_namesurname" path="businessname" cssStyle="width:300px;"/>
                </td>
                </tr>

以及实体领域等等。 该实体有一些相关的实体,通过外键连接(如种植体,植入反射器,种植体)。

提交给该控制器:

 @RequestMapping(value = "/admin/finalizeModifyImplant/{id}",
method = { RequestMethod.GET,RequestMethod.POST })
public ModelAndView finalizeModifyImplant(@PathVariable int id, @Valid @ModelAttribute Implant implant, BindingResult result){
    ModelAndView mav=new ModelAndView();
    mav.setViewName("../../admin/modifyImplant/"+id);
    if (result.hasErrors()){
        return mav;
    }

    implant.setId(id); //NOTICE WHAT I NEED TO DO HERE!
    Implant oldImplant= impDAO.findById(id);
    implant.setImplantinverters(oldImplant.getImplantinverters());
    implant.setImplantpanels(oldImplant.getImplantpanels());
    implant.setImplanttype(oldImplant.getImplanttype());
    implant.setInverters(oldImplant.getInverters());
    implant.setPvgis(oldImplant.getPvgis());
    try{
        impDAO.merge(implant); //here i call getSession().merge()
    }catch(RuntimeException re){
        return mav;
        //TODO: errors
    }

当我收到表单提交(这是更新)时,我遇到以下问题: 1.返回的植入物没有id(字段id = null) 2.相关实体也为空。

以下是植入实体

public class Implant implements java.io.Serializable {
// Fields
private Integer id;
private Pvgis pvgis;
private Gateway gateway;
private Implanttype implanttype;
@JsonIgnore //I NEED TO JSONIGNORE IT BECAUSE IT HAS A RECURSIVE CALL ON IMPLANT
private Users users;
@NotEmpty
private String nameimplant;
private String place;
private double latitude;
private double longitude;
private double expectedpower;
private double tolerance;
[...] and many other fields

和相关的implant.hbm.xml

<hibernate-mapping>
<class name="it.pstmarche.model.Implant" table="implant" schema="public">
    <id name="id" type="integer">
        <column name="id" />
        <generator class="identity" />
    </id>
    <many-to-one name="pvgis" class="it.pstmarche.model.Pvgis" fetch="select">
        <column name="idpvgis" />
    </many-to-one>
    <many-to-one name="gateway" class="it.pstmarche.model.Gateway" fetch="select">
        <column name="idgateway" />
    </many-to-one>
    <many-to-one name="implanttype" class="it.pstmarche.model.Implanttype" fetch="select">
        <column name="implanttype" />
    </many-to-one>
    <many-to-one name="users" class="it.pstmarche.model.Users" fetch="select" >
        <column name="iduser" />
    </many-to-one>
    <property name="nameimplant" type="string">
        <column name="nameimplant" not-null="true">
            <comment>Name</comment>
        </column>

我真的无法想象如何正确地进行更新,因为当我尝试从db(通过findById或直接查询)获取更新的实体时,我得到随机结果,有时我得到正确和更新的实体,有时我得到旧实体!!真是随意的。

我试过了: 1.删​​除没有结果的缓存(session.setCacheMode(CacheMode.IGNORE);) 2.添加getSession()。flush();的getSession()close()方法。在合并方法的最后,那:

public void merge(Implant currentImp){
    //Session session = HibernateSessionFactory.getSessionFactory().openSession();
    Transaction tx= getSession().beginTransaction();
    if (currentImp.getId() != null) { // it is an update
        getSession().merge(currentImp);
        tx.commit();
        //getSession().update(currentImp);
        log.debug("MERGE");
        getSession().flush();// THAT'S THE ADD
        session.close(); //THIS IS ALSO
    } else { // you are saving a new one
        getSession().saveOrUpdate(currentImp);
        tx.commit();
        getSession().flush();
        getSession().close();
        log.debug("Save sou");
    }

我真的无法确定使用Hibernate + Spring实现更新的正确方法是什么!任何帮助表示赞赏。感谢

编辑:也许我的问题不明确。目前的问题是,在我保存或合并一个实体后,随机地,当我搜索实体时(比如通过查询findById(int id)),我随机得到一个旧实体(我认为它仍然是在Hibernate会话中)。 覆盖此问题的唯一方法是清除会话(getSession()。clear()),但在此之后我显然会遇到懒惰异常 - 没有代理当我尝试获取我需要的实体的相关实体在清除的会话中!这是一个耻辱

2EDIT :谢谢Tim H.但是,我仍然面临与处理hibernate会话相关的问题。 前言:我使用每线程的hibernate会话,比如

private static final ThreadLocal<Session> threadLocal = new ThreadLocal<Session>();
private  static Configuration configuration = new Configuration();    
private static org.hibernate.SessionFactory sessionFactory;
private static String configFile = CONFIG_FILE_LOCATION;
static {
    try {
        configuration.configure(configFile);
        sessionFactory = configuration.buildSessionFactory();
    } catch (Exception e) {
        System.err
                .println("%%%% Error Creating SessionFactory %%%%");
        e.printStackTrace();
    }
}
private HibernateSessionFactory() {
}
public static Session getSession() throws HibernateException {
    Session session = (Session) threadLocal.get();
    if (session == null || !session.isOpen()) {
        if (sessionFactory == null) {
            rebuildSessionFactory();    
        }
        session = (sessionFactory != null) ? sessionFactory.openSession()
                : null;
        threadLocal.set(session);
    }
    return session;
}

现在,假设我在Tomcat线程1 上从db加载实体x。实体值 100 。加载之后我无法关闭会话,因为它是懒惰的初始化(如果我关闭会话,我冒险以获得延迟初始化异常 - 无代理/会话)。< / p>

之后,我在线程2上更新实体x的值。值现在为50

最后,我加载回来的值,但tomcat让我进入线程1,其中实体仍然在线程的会话中,值仍然是100.我得到旧值而不是新值!! < / p>

所以,我不能关闭会话,直到我的jsp完成(我使用其上的惰性字段),我必须做这个反模式来关闭jsp结束时的会话:&lt;%HibernateSessionFactory.getSession ()。关(); %GT;那太可怕了!我将视图与模型结合在一起!有一种安全的模式吗?

2 个答案:

答案 0 :(得分:1)

我或许可以解释你的一些问题。

  1. 如果请求参数与您实体的id属性(即隐藏的表单字段)匹配,则您的ID将仅绑定到您的实体。

  2. 对于其余的绑定,您可能希望使用所谓的“@ModelAttribute”技巧。基本上任何使用@ModelAttribute注释的方法都将在所有请求之前被调用,因此这个方法将返回一个实体,然后Spring将从表单中绑定您的支持对象,就像这样。

    @ModelAttribute植入种植体 public Implant getBackingImplant(HttpServletRequest request) {   if(request.getParameter(“id”)!= null     return dao.getImplantByIdWithAllMyRelationshipsFilledIn(request.getParameter(“id”);

    返回新的Implant(); }

  3. 所以现在spring会将你的表单绑定到getBackingImplant返回的实体。

    至于你的保存没有工作,我真的不确定那个我不是一个休眠的家伙,但上面应该解决你的形式绑定困境。

答案 1 :(得分:0)

添加新答案,因为问题现在更加明确。记住merge返回它合并的实体,你丢弃了merge命令的结果,而是你应该将它返回给你的控制器。

相关问题