1

I know there are loads of entries about this topic. I have already read all of them searching for the problem I am facing.

I have a class/table with a self-reference. This is the Class

public class Comment {

    private Integer id;
    private Comment parent;
    private Issue   issue;
    private User    author;
    private String  body;
    private Date    created;
    private Date    updated;

    private Set<Comment> childs;

    // All setters and getters
}

And here you can see the hbm.xml file:

<hibernate-mapping>
 <class name="Comment" table="COMMENTS">

  <id name="id" type="java.lang.Integer">
   <column name="ID"/>
   <generator class="native"/>
  </id>

  <many-to-one cascade="all"
   class="Comment" fetch="join" name="parent">
   <column name="PARENT" not-null="false"/>
  </many-to-one>

  <many-to-one class="Issue"
   fetch="join" name="issue">
   <column name="ISSUE" not-null="true"/>
  </many-to-one>

  <many-to-one class="User"
   fetch="join" name="author">
   <column name="AUTHOR" not-null="true"/>
  </many-to-one>

  <property generated="never" lazy="false" name="body" type="java.lang.String">
   <column name="BODY" not-null="true"/>
  </property>

  <property generated="never" lazy="false" name="created" type="java.util.Date">
   <column name="CREATED"/>
  </property>

  <property generated="never" lazy="false" name="updated" type="java.util.Date">
   <column name="UPDATED"/>

  </property>
  <set cascade="delete" fetch="select" inverse="true" lazy="true"
   name="childs" sort="unsorted" table="COMMENTS">
   <key>
    <column name="ID" not-null="true"/>
   </key>
   <one-to-many class="Comment"/>
  </set>

 </class>

</hibernate-mapping>

So far everything is fine. But I must have an error somewhere because, when I am running this unit test

Session session = sessionFactory.getCurrentSession();

User user = new User("loginName", "password", "firstName", "lastName", "eMail");
session.save(user);
session.flush();
session.clear();

Issue issue = new Issue();
session.save(issue);
session.flush();
session.clear();

Comment parent = new Comment(issue, user, "body_parent");
session.save(parent);

Comment child = new Comment(issue, user, "body_child_1");
child.setParent(parent);
parent.getChilds().add(child);
session.save(child);
session.flush();
session.clear();

parent = (Comment) session.createQuery("from Comment comment where comment.body='body_parent'").uniqueResult();

System.out.println(parent);
System.out.println(parent.getChilds().iterator().next());

I added the last two lines in order to show you the problem I am facing right now:

COMMENT = [id=1, parentId=<root>, issueId=1, authorId=1, body=body_parent, created=2014-03-08 19:28:54.832, updated=2014-03-08 19:28:54.832, numChilds=1]
COMMENT = [id=1, parentId=<root>, issueId=1, authorId=1, body=body_parent, created=2014-03-08 19:28:54.832, updated=2014-03-08 19:28:54.832, numChilds=1]

Both parent and child are the same! I don't really understand the problem here. When I retrieve the child through a query the result is correct but when I get the child through getChild() there's not query to retrieve the its childs.

Have you any idea? Any clue? I do not see the light at the end of the tunnel here :-/

Thanks a lot in advance!

kazbeel
  • 1,378
  • 19
  • 40
  • this does not answers your question, but `xml` is evil, use `annotations` – Svetlin Zarev Mar 08 '14 at 19:16
  • @SvetlinZarev Could you elaborate on how XML is evil? – Bart Mar 09 '14 at 19:29
  • Your relation is backwards by the way. It should be one-to-many instead of many-to-one. – Bart Mar 09 '14 at 19:34
  • @Bart It's difficult to read/write/understand and least but not last - when you look at your entity it tells you nothing about the persistence layer and you have to look-up everything you want to know from that ugly, unreadable pile of **** called xml. If you are using Java 1.5+ I cannot find any reason to prefer xml over annotations. That's it - nothing more and nothing less :) – Svetlin Zarev Mar 09 '14 at 19:59
  • The purpose of this project is merely for learning. I started doing everything with hbm.xml and I don't really think in changing to Annotations... – kazbeel Mar 09 '14 at 20:05
  • 2
    @SvetlinZarev Good points. I guess evil is not the right word. XML is not recommended since annotations are more declarative. On the other side XML does not clutter your code with annotations. So I guess both have benefits but for general use annotations are preferred. – Bart Mar 09 '14 at 20:17

3 Answers3

2

I finally have realized what the problem was! Well, there was couple of problems.

First, there was no parent_id field in neither the DB nor the class. Second, there was no exception because I activated inverse property to the set.

The combination of both issues was the problem.

Below, I post the working solution.

Comment.java

public class Comment {
    private Integer      id;
    private Integer      parentId; // Parent ID was missing
    private Issue        issue;
    private User         author;
    private String       body;
    private Date         created;
    private Date         updated;

    private Comment      parent; // Reference to parent though parentId
    private Set<Comment> childs = new HashSet<Comment>(0);


    protected Comment () {

    }

   // All setters and getters
}

Comment.hbm.xml

<hibernate-mapping>
 <class name="es.kazbeel.geckobugtracker.model.Comment" table="COMMENTS">

  <id name="id" type="java.lang.Integer">
   <column name="ID"/>
   <generator class="native"/>
  </id>

  <property name="parentId" type="java.lang.Integer" update="false" insert="false" column="PARENT_ID" />

  <many-to-one class="es.kazbeel.geckobugtracker.model.Issue"
   fetch="join" name="issue">
   <column name="ISSUE"/>
  </many-to-one>

  <many-to-one class="es.kazbeel.geckobugtracker.model.User"
   fetch="join" name="author">
   <column name="AUTHOR"/>
  </many-to-one>

  <property generated="never" lazy="false" name="body" type="java.lang.String">
   <column name="BODY"/>
  </property>

  <property generated="never" lazy="false" name="created" type="java.util.Date">
   <column name="CREATED"/>
  </property>

  <property generated="never" lazy="false" name="updated" type="java.util.Date">
   <column name="UPDATED"/>
  </property>

  <many-to-one name="parent" class="es.kazbeel.geckobugtracker.model.Comment" column="PARENT_ID" not-null="false" />

  <set name="childs" table="COMMENTS"  lazy="false" cascade="all-delete-orphan" inverse="false">
   <key column="PARENT_ID" />
   <one-to-many class="es.kazbeel.geckobugtracker.model.Comment"/>
  </set>

 </class>
</hibernate-mapping>

I hope this is helpful for someone in the future. This is the first time I post a self-solution :)

kazbeel
  • 1,378
  • 19
  • 40
1

Take a shot using the code below:

Comment parent = new Comment(issue, user, "body_parent");
Comment child = new Comment(issue, user, "body_child_1");
child.setParent(parent);
parent.getChilds().add(child);
session.save(parent);
session.flush();
session.clear();
Jiang
  • 590
  • 2
  • 10
  • I get this exception `org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing` as the child is not saved... – kazbeel Mar 08 '14 at 19:24
  • try a cascade insert – Jiang Mar 08 '14 at 19:31
1

from this

your issue will be resolved by properly defining cascading depedencies or by saving the referenced entities before saving the entity that references. Defining cascading is really tricky to get right because of all the subtle variations in how they are used.

cascade="delete"

check the options all|none|save-update|delete|all-delete-orphan

session.save(child); instead of this try cascade="all" and session.save(parent) so that all child objects will be saved with parent.

Community
  • 1
  • 1
Malatesh
  • 1,944
  • 6
  • 26
  • 39
  • I tried everything related to "cascade" but with no results. I believe the problem resides somewhere else. As I highlighted, both parent and child are persisted in the DB, the problem is that I am not able to retrieve the child of the parent through `getChilds()`. In fact, calling the function does not fire any new query on the DB, that's actually the principal problem. I'm sure this is a silly problem, I don't see where though. – kazbeel Mar 10 '14 at 06:41