2

I have this problem:

i prepare the view with the entity

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


}

This code approach to a jsp ( a little portion of code)

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

and so on for the fields of entity. The entity has some entity related, connected by foreign key ( like implanttype, implantinverter, implantpanels ).

the submission go to this controller:

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

when i get the form submission (this is an update) i have the following problems: 1. the returning implant has no id (field id=null) 2. the related entities are also null.

following is the implant entity

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

and the related 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>

i really cannot figure how to do correctly the update, because when i try to get the updated entity from the db (via findById or directly a query) i get random result, sometimes i get the correct and updated entity, sometimes i get the old entity!! Really random.

I tried: 1. removing the cache ( session.setCacheMode(CacheMode.IGNORE); ) with no result 2. adding getSession().flush(); getSession().close(); at the end of the merge method, that:

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

I really cannot figure what is the correct way to implement updates with Hibernate + Spring! Any help is appreciated. Thanks

EDIT: Maybe my question was not clear. The current problem is, after i save or merge an entity, randomly, when i search the entity (like by a query findById(int id)) i get randomly the old entity (that i think it's still in the Hibernate session). The only way to override this problem is clearing the session (getSession().clear()), but after this i obviously get Lazy exceptions - no proxy when i try to get the related entities of the entity i need that is in the cleared session! that's a shame

2EDIT: Thanks Tim H. However, i still facing issues related to handling hibernate sessions. preface: i use hibernate session per-thread, like

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

Now, suppose i load entity x from the db, on Tomcat thread 1. The entity value is 100. I cannot close the session after loading it because it's lazy initialized(and if i close the session, i risk to get a lazy initialization exception - No Proxy/session).

After, i update the value of entity x on thread 2. The value now is 50.

Finally, i load back the value, but tomcat got me to thread 1, where the entity is still in the session of the thread, and the value is still 100. I get the old value instead the new one!!

So, i cannot close the session until my jsp is finished (i use the lazy fields on that), i have to do this anti-pattern to close the session at the end of jsp via: <% HibernateSessionFactory.getSession().close(); %> and that is horrible! i couple the view with the model! there is a pattern to do this safely??

Fzan
  • 75
  • 11

2 Answers2

1

I may be able to shed light on a few of your problems.

  1. Your id will only be bound to your entity if a request parameter matches your id property on your entity (ie a hidden form field).

  2. For the rest of the binding you might want to use what is referred to as the "@ModelAttribute" trick. Basically any method annotated with @ModelAttribute will be called before all requests, so this method will return the entity that Spring will then form bind your backing object from your form against, like so.

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

    return new Implant(); }

So now spring will bind your form against the entity returned by getBackingImplant.

As far as your save not working, I'm really not sure on that one I'm not a hibernate guy, but the above should solve your form binding woes.

Tim H
  • 338
  • 1
  • 2
  • 8
  • Hi @Tim H and thanks for the answer. Sadly, i already have tried the point 1 with (at)RequestMapping(value = "/admin/finalizeModifyImplant/{id}", method = { RequestMethod.GET, RequestMethod.POST }) public ModelAndView finalizeModifyImplant((at)PathVariable int id, (at)Valid (at)ModelAttribute Implant implant, BindingResult result) so i already do the validation check against the data, but the resulting implant still miss the related entities, that are populated with NULL and not maybe with the $$_javassist because they are lazy. Maybe i just missed your point? – Fzan Apr 19 '12 at 07:44
  • Updated the question with the current big problem – Fzan Apr 19 '12 at 09:35
0

Adding a new answer, since the problem is more clear now. Remember merge returns the entity it merged, you are throwing away the result of you merge command, instead you should return that back to your controller.

Tim H
  • 338
  • 1
  • 2
  • 8
  • edited the question. the merged item still not have the correct related entity fields, sadly. – Fzan Apr 19 '12 at 14:46
  • You're outside of my realm of knowledge now with hibernate. See this thread on how to keep sessions alive in a view - http://stackoverflow.com/questions/1139985/in-spring-with-jpa-hibernate-how-do-i-keep-a-session-open-to-avoid-lazy-initial – Tim H Apr 19 '12 at 17:22