Hibernate Books

All times are UTC - 5 hours [ DST ]



Post new topic Reply to topic  [ 7 posts ] 
Author Message
 Post subject: Load, delete and then persist it
PostPosted: Thu Sep 04, 2003 9:12 am 
Newbie

Joined: Fri Aug 29, 2003 9:12 am
Posts: 5
Hello everybody

I have a parent child relation. I attached the .hbm.xml file at the end of this post. I have this relation persisted in the db. If I load the parent [obj = load(...)] from the db, then delete the parent [session.delete(parent)] , and then in another session I try to persist the same obj object in the db I get an exception:

java.lang.NullPointerException
at net.sf.hibernate.impl.SessionImpl.updateReachableCollection(C:/work/hybernate/src/net/sf/hibernate/impl/SessionImpl.java:2530)
at net.sf.hibernate.impl.SessionImpl.updateReachable(C:/work/hybernate/src/net/sf/hibernate/impl/SessionImpl.java:2568)
at net.sf.hibernate.impl.SessionImpl.updateReachables(C:/work/hybernate/src/net/sf/hibernate/impl/SessionImpl.java:2648)
at net.sf.hibernate.impl.SessionImpl.flushEntities(C:/work/hybernate/src/net/sf/hibernate/impl/SessionImpl.java:2248)
at net.sf.hibernate.impl.SessionImpl.flushEverything(C:/work/hybernate/src/net/sf/hibernate/impl/SessionImpl.java:2033)
at net.sf.hibernate.impl.SessionImpl.flush(C:/work/hybernate/src/net/sf/hibernate/impl/SessionImpl.java:2020)
at net.sf.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:57)

I have managed to resolve this situation but it's not clear to me if what I do is good and why should I do that. I have set the id of all the children to null.
I have also observed that when it comes to a flush, all the collections of the changed entities are updated. The collections are discovered from the owning entity fields. When I have a collection which is null, Hibernate stores it as a []. So the method SessionImpl.getCollectionEntry(PersistentCollection coll) is called on a [] object. This method returns null and when updateReachableCollection() method tries to acces the Collection Entry this is null, so that's it.

Could someone illuminate me please?

Thank you.



Attachement (.hbm.xml):

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">

<hibernate-mapping>
<class
name="hibernate.Organisation"
table="ORGANISATIONS"
dynamic-update="false"
dynamic-insert="false"
>

<id
name="id"
column="ORGANISATION_ID"
type="java.lang.Long"
>
<generator class="native">
</generator>
</id>

<many-to-one
name="parentId"
class="hibernate.Organisation"
cascade="none"
outer-join="auto"
update="true"
insert="true"
column="PARENT_ID"
not-null="false"
/>

<property
name="name"
type="java.lang.String"
update="true"
insert="true"
column="NAME"
length="1000"
not-null="true"
unique="true"
/>


<bag
name="suborganisations"
lazy="false"
inverse="true"
cascade="all"
>

<key
column="PARENT_ID"
/>

<one-to-many
class="hibernate.Organisation"
/>
</bag>

</class>

</hibernate-mapping>


Top
 Profile  
 
 Post subject:
PostPosted: Thu Sep 04, 2003 11:17 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
Ummmm I'm not really sure exactly what you are trying to tell me here, but it does look like you are doing something at least a little bit "funny".


Anyway, Hibernate shouldn't throw NPEs, so submit a simple main() method to JIRA, and I will take a look and at least fix the npe.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Sep 05, 2003 4:39 am 
Newbie

Joined: Fri Aug 29, 2003 9:12 am
Posts: 5
Here is the Organisation.java:
Code:
/*
* COPYRIGHT
*/
package hibernate;


import java.util.List;
import java.util.ArrayList;


/**
* @author mslv
*
* @hibernate.class
*  table="ORGANISATIONS"
*/
public class Organisation {

  private Long id;

  private Organisation parentId;

  private List suborganisations;

  private String name;

  /**
   * @hibernate.id
   *  generator-class="native"
   *  column="ORGANISATION_ID"
   */
  public Long getId() {
    return id;
  }

  public void setId(Long _id) {
    this.id = _id;
  }

  /**
   * @hibernate.many-to-one
   *  column="PARENT_ID"
   *  not-null="true"
   */
  public Organisation getParentId() {
    return parentId;
  }

  public void setParentId(Organisation parentId) {
    this.parentId = parentId;
  }

  /**
   * @hibernate.property
   *  column="NAME"
   *  length="1000"
   *  not-null="true"
   *  unique="true"
   */
  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public List getSuborganisations() {
    return suborganisations;
  }

  /**
   * @hibernate.bag
   *  inverse="true"
   *  lazy="false"
   *  cascade="all"
   *  @hibernate.collection-key
   *    column="PARENT_ID"
   *  @hibernate.collection-one-to-many
   *    class="hibernate.Organisation"
   */
  public void setSuborganisations(List suborganisations) {
    this.suborganisations = suborganisations;
  }

  public void addSubOrganisation(Organisation org) {
    if (suborganisations == null) {
      suborganisations = new ArrayList();
    }
    suborganisations.add(org);
  }

  public void removeSubOrganisation(Organisation org) {
    suborganisations.remove(org);
  }
}


The generated .hbm.xml is in the first post.

The main is like this:
Code:
/*
* COPYRIGHT
*/
package hibernate;


import net.sf.hibernate.cfg.Configuration;
import net.sf.hibernate.*;
import net.sf.hibernate.tool.hbm2ddl.SchemaExport;

import java.util.ArrayList;
import java.util.List;
import java.util.Iterator;
import java.sql.SQLException;


public class OrganisationMain {

  private SessionFactory _sessions;

  public void configure() throws HibernateException {
    _sessions = new Configuration()
        .addClass(Organisation.class)
        .buildSessionFactory();
  }

  public void exportTables() throws HibernateException {
    Configuration cfg = new Configuration()
        .addClass(Organisation.class);
    new SchemaExport(cfg).create(true, true);
  }

  public Organisation createOrganisation(String name, List organizationList) throws HibernateException {

    Organisation org = new Organisation();
    org.setName(name);
    org.setSuborganisations(organizationList);
    org.setParentId(null);

    Session session = _sessions.openSession();
    Transaction tx = null;
    try {
      tx = session.beginTransaction();
      session.save(org);
      tx.commit();
    }
    catch (HibernateException he) {
      if (tx != null) tx.rollback();
      throw he;
    }
    finally {
      session.close();
    }
    return org;
  }

  /**
   * Creates a suborganisation knowing only the parent organisation.
   * @param parentId the parent org
   * @return the child org
   * @throws HibernateException
   */
  public Organisation createSubOrganisation(Long parentId) throws HibernateException {
    Organisation child = null;
    Session session = _sessions.openSession();
    Transaction tx = null;
    try {
      tx = session.beginTransaction();

      Organisation org = (Organisation) session.load(Organisation.class, parentId);

      child = new Organisation();
      child.setName("child");
      child.setParentId(org);
      child.setSuborganisations(null);

      org.addSubOrganisation(child);

      tx.commit();
    }
    catch (HibernateException he) {
      if (tx != null) tx.rollback();
      throw he;
    }
    finally {
      session.close();
    }
    return child;
  }

  public void deleteOrganisation(Long orgId) throws HibernateException {
    Session session = _sessions.openSession();
    Transaction tx = null;
    Organisation org = null;
    try {
      tx = session.beginTransaction();
      org = (Organisation) session.load(Organisation.class, orgId);
      session.delete(org);
      tx.commit();
    }
    catch (HibernateException he) {
      if (tx != null) tx.rollback();
      throw he;
    }
    finally {
      session.close();
    }

    session = _sessions.openSession();
    try {
      tx = session.beginTransaction();
      session.save(org);
      tx.commit();
    }
    catch (HibernateException he) {
      if (tx != null) tx.rollback();
      throw he;
    }
    finally {
      session.close();
    }
  }

public static void main(String[] args) {
    OrganisationMain orgMain = new OrganisationMain();
    try {
      orgMain.configure();
      orgMain.exportTables();
      Organisation rootOrg = orgMain.createOrganisation("Root org", null);
      Organisation childOrg = orgMain.createSubOrganisation(rootOrg.getId());
      orgMain.deleteOrganisation(rootOrg.getId());
    }
    catch (HibernateException e) {
      e.printStackTrace();
    }

  }
}


Top
 Profile  
 
 Post subject:
PostPosted: Fri Sep 05, 2003 4:46 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
Please submit some working code to JIRA. I do not want to spend an hour getting the code running from what you have included in a forum post!


Top
 Profile  
 
 Post subject:
PostPosted: Fri Sep 05, 2003 5:13 am 
Newbie

Joined: Fri Aug 29, 2003 9:12 am
Posts: 5
Sorry, but this code works fine. I just copied it from the post and it's ok. Well it throws a null pointer eception in a hibernate class, but that was just what I intended to show. I wonder if you could tell me what did not work when you tried it?

Also if you generated the .hbm.xml file with xDoclet then you should change the Organisation.java file with the following code (there was a not null condition):
Code:
/*
* COPYRIGHT
*/
package hibernate;


import java.util.List;
import java.util.ArrayList;


/**
* @author mslv
*
* @hibernate.class
*  table="ORGANISATIONS"
*/
public class Organisation {

  private Long id;

  private Organisation parentId;

  private List suborganisations;

  private String name;

  /**
   * @hibernate.id
   *  generator-class="native"
   *  column="ORGANISATION_ID"
   */
  public Long getId() {
    return id;
  }

  public void setId(Long _id) {
    this.id = _id;
  }

  /**
   * @hibernate.many-to-one
   *  column="PARENT_ID"
   *  not-null="false"
   */
  public Organisation getParentId() {
    return parentId;
  }

  public void setParentId(Organisation parentId) {
    this.parentId = parentId;
  }

  /**
   * @hibernate.property
   *  column="NAME"
   *  length="1000"
   *  not-null="true"
   *  unique="true"
   */
  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public List getSuborganisations() {
    return suborganisations;
  }

  /**
   * @hibernate.bag
   *  inverse="true"
   *  lazy="false"
   *  cascade="all"
   *  @hibernate.collection-key
   *    column="PARENT_ID"
   *  @hibernate.collection-one-to-many
   *    class="hibernate.Organisation"
   */
  public void setSuborganisations(List suborganisations) {
    this.suborganisations = suborganisations;
  }

  public void addSubOrganisation(Organisation org) {
    if (suborganisations == null) {
      suborganisations = new ArrayList();
    }
    suborganisations.add(org);
  }

  public void removeSubOrganisation(Organisation org) {
    suborganisations.remove(org);
  }
}



Top
 Profile  
 
 Post subject:
PostPosted: Fri Sep 05, 2003 6:08 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
Working code includes

* mapping files or a proper fully working XDoclet build!
* configuration files


ie. it is in a runnable state!


Next time, please submit a working project to JIRA. That is the process we follow here.



Ok. The reason for the exception is this:


When you delete the top organization, the delete cascades to its child. In particular the child's collection is marked as deleted.

When you re-save the top organization in a new session, we cascade save-update to the child. Since the child has an non-null id, Hibernate decides to pass it to update().

Now we are in a pathological situation where hibernate is confused about the state of the collection. Hibernate *should* do some better error detection than an NPE, clearly, so i will fix that.


You may fix your problem by setting the id of the child organization to null, before attempting to re-save it, so you don't confuse Hibernate's isUsaved() algorithm.

Note that delete-then-resave is NOT a recommended idiom in Hibernate. (Hardly surprising, since it is virtually useless - it is always better to update() in this case.)


Top
 Profile  
 
 Post subject: Re:
PostPosted: Fri Jun 29, 2012 3:34 pm 
Newbie

Joined: Thu Jan 26, 2012 11:56 am
Posts: 9
gavin wrote:

You may fix your problem by setting the id of the child organization to null, before attempting to re-save it, so you don't confuse Hibernate's isUsaved() algorithm.


o.O but he had said he had set to null all the children

mslv wrote:
I have managed to resolve this situation but it's not clear to me if what I do is good and why should I do that. I have set the id of all the children to null.

_________________
-------------------------------------------------------------
http://javahelp.redsaltillo.net


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 7 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.