【问题标题】:Correct way to update entities on Hibernate + Spring在 Hibernate + Spring 上更新实体的正确方法
【发布时间】:2012-04-18 13:38:23
【问题描述】:

我有这个问题:

我用实体准备视图

@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>

实体的字段以此类推。 实体有一些实体相关,通过外键连接(如implanttype、implantinverter、implantpanels)。

提交到这个控制器:

 @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>

我真的不知道如何正确进行更新,因为当我尝试从数据库(通过 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)),我随机得到旧实体(我认为它仍然是在休眠会话中)。 解决此问题的唯一方法是清除会话 (getSession().clear()),但在此之后我显然得到了延迟异常 - 没有代理,当我尝试获取清除会话中我需要的实体的相关实体!太可惜了

2EDIT:感谢 Tim H。不过,我仍然面临与处理休眠会话相关的问题。 前言:我使用每个线程的休眠会话,比如

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 上从数据库加载实体 x。实体值为100。加载后我无法关闭会话因为它是延迟初始化的(如果我关闭会话,我有风险得到延迟初始化异常 - 无代理/会话)。

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

最后,我加载了值,但是 tomcat 让我进入线程 1,实体仍在线程的会话中,值仍然是 100。我得到的是旧值而不是新值!!

所以,在我的 jsp 完成之前我无法关闭会话(我使用惰性字段),我必须通过以下方式在 jsp 结束时关闭会话: 这太可怕了!我将视图与模型结合起来!有一种模式可以安全地执行此操作吗??

【问题讨论】:

    标签: spring hibernate caching merge


    【解决方案1】:

    我或许能够阐明您的一些问题。

    1. 只有在请求参数与您的实体上的 id 属性(即隐藏的表单字段)匹配时,您的 id 才会绑定到您的实体。

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

      @ModelAttribute Implant 植入 public Implant getBackingImplant(HttpServletRequest 请求) { if(request.getParameter("id") != null return dao.getImplantByIdWithAllMyRelationshipsFilledIn(request.getParameter("id");

      return new Implant(); }

    所以现在 spring 会将您的表单与 getBackingImplant 返回的实体绑定。

    至于你的保存不起作用,我真的不确定我不是一个休眠的人,但上面应该解决你的表单绑定问题。

    【讨论】:

    • 嗨@Tim H,感谢您的回答。可悲的是,我已经用 (at)RequestMapping(value = "/admin/finalizeModifyImplant/{id}", method = { RequestMethod.GET, RequestMethod.POST }) public ModelAndView finalizeModifyImplant((at)PathVariable int id 尝试了第 1 点, (at)Valid (at)ModelAttribute Implant 植入,BindingResult 结果)所以我已经对数据进行了验证检查,但生成的植入仍然错过了相关实体,这些实体填充了 NULL 而不是 $$_javassist 因为他们很懒惰。也许我只是错过了你的意思?
    • 用当前的大问题更新了问题
    【解决方案2】:

    添加一个新答案,因为现在问题更清楚了。请记住,merge 返回它合并的实体,您正在丢弃合并命令的结果,而是应该将其返回给您的控制器。

    【讨论】:

    • 编辑了问题。遗憾的是,合并后的项目仍然没有正确的相关实体字段。
    • 你现在已经超出了我的知识范围,现在有了hibernate。请参阅此线程,了解如何在视图中保持会话 - stackoverflow.com/questions/1139985/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-16
    • 1970-01-01
    相关资源
    最近更新 更多