-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 8 posts ] 
Author Message
 Post subject: [solved] @ManyToOne and @OneToMany; index position of list ?
PostPosted: Sat May 05, 2007 7:12 am 
Newbie

Joined: Thu May 03, 2007 6:24 am
Posts: 8
Hibernate 3.2.3, Hibernate Annotations 3.3.0

This subject has shown up MANY MANY times in this forum and throughout the documentations.
I am sorry to bring it up again, but even though I was reading many hours through the documentation/forum, I have not *understood* WHY we need to model this relationship in certain ways. I hope for EXPLANATIONS rather than a simple solutions that I have to treat as a "black box" ;)

Given this scenario:

(a) This does NOT work

Code:
@Entity
public class Parent {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long parentID;

    @OneToMany(mappedBy = "childID", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @IndexColumn(name = "position", base=0)
    private List<Child> children = new ArrayList<Child>();
}

@Entity
public class Child {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long childID;

    @ManyToOne
    private Parent parent;
}

In this mapping the owner side of the parent-child relation is the child. The parentID is also nicely kept in the Child table, BUT the position is NULL!
This has been described many times in this forum, that an extra association table is necessary which I show in (b) now.
However, *WHY* do we need to do so ? Why does it not work with this mapping ? Can someone EXPLAIN ?

(b) This does (!) work

Code:
@Entity
public class Parent {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long parentID;

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @IndexColumn(name = "position", base=0)
    private List<Child> children = new ArrayList<Child>();
}

In this case, the mappedBy keyword in the annotation is gone, and hence the relation is expressed in an association table. That is the common "work-a-round".

(c) Something NEW: The *position* of the child in the list is often solved via this solution.

Code:
public int getPosition() {
    return parent.getChildren().indexOf(this)
}


This, again, looks like a work-a-round solution.

Since Hibernate 3 the proper ManyToOne and OneToMany can be expressed as we can see in (b). The position is nicely stored in the table, so HOW can I utilize this without this ugly parent.getChildren().indexOf(this) solution which might fetch more objects than I actually wanted ;)

What would be my Annotation for the position field in the child ? I was not able to figure this out with all the documentation/forum/etc available.

It would be nice if we can discuss this subject a bit further. Thank you very much!


Last edited by markusbr on Sat May 05, 2007 1:24 pm, edited 1 time in total.

Top
 Profile  
 
 Post subject:
PostPosted: Sat May 05, 2007 7:36 am 
Hibernate Team
Hibernate Team

Joined: Mon Aug 25, 2003 9:11 pm
Posts: 4592
Location: Switzerland
There are no workarounds or association tables necessary. Just copy and paste the example from the documentation:

http://hibernate.org/hib_docs/annotatio ... -extratype

You need to understand what "inverse" (or "mappedBy", which is the same) means. There are plenty of documents around, including a whole page on the wiki.

_________________
JAVA PERSISTENCE WITH HIBERNATE
http://jpwh.org
Get the book, training, and consulting for your Hibernate team.


Top
 Profile  
 
 Post subject:
PostPosted: Sat May 05, 2007 8:34 am 
Newbie

Joined: Thu May 03, 2007 6:24 am
Posts: 8
christian wrote:
There are no workarounds or association tables necessary. Just copy and paste the example from the documentation:

http://hibernate.org/hib_docs/annotatio ... -extratype

You need to understand what "inverse" (or "mappedBy", which is the same) means. There are plenty of documents around, including a whole page on the wiki.

Hi Christian

Many thanks for pointing me to this section. I read this section several times, I will show you what happens in my scenarios...

And BTW: Section 2.4.6.2.3 of this documentation is very confusing. I assume the 2nd class in the first examples is the CHILD rather than parent.
Anyhow, back to "my" problem.

a, b, c is referring back to my code in the first post!

(a) INVERSE
Here the following table structure is created:
Parent(*parentID)
Child(*childID, parentID, position)

Ideal, the table structure at least.
childIDs and parentIDs are inserted.
BUT the position remain always always 0.
WHY :((( ???

(b)
Here the following table structure is created:
Parent(*parentId)
Parent_Child(*parentID, childID, *position)
Child(*childID, parentID, parent_parentID)

This introduces some redundancy which certainly is far from ideal.
However, the position is now actually filled in properly ;)

And this solution is in line with the documentation http://hibernate.org/hib_docs/annotatio ... -extratype

Am I missing something ? That can't be right...

(c)
I'd be VERY great full if one can point out to me how to obtain the rank from the Child a bit more elegant than "parent.getChildren().indexOf(this)".
What Annotation do I need to *query* the rank from the DB ?
Code:
@Entity
public class Child {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long childID;

    @ManyToOne
    private Parent parent;

    @NO IDEA HOW TO ANNOTATE THIS TO GET THE INDEX FROM THE DB!
    private position;
}

Sorry to bother with this for you maybe simple/silly stuff. Everything works great for me, my whole mapping works, except this little small annoying thing which I can't solve properly.

I hope for yet another hint. Thank you so much!


Top
 Profile  
 
 Post subject:
PostPosted: Sat May 05, 2007 9:55 am 
Hibernate Team
Hibernate Team

Joined: Mon Aug 25, 2003 9:11 pm
Posts: 4592
Location: Switzerland
Code:
@Entity
@Table(name="PARENT_TABLE")
public class Parent {

    @OneToMany
    @JoinColumn(name="PARENT_ID", nullable=false)
    @org.hibernate.annotations.IndexColumn("CHILD_POSITION")
    private List<Child> children;
    ...
}

@Entity
@Table(name="CHILD_TABLE")
public class Child {
    ...
    @ManyToOne
    @JoinColumn(name="PARENT_ID", insertable=false, updatable=false, nullable=false)
    private Parent parent;
    ...
}

_________________
JAVA PERSISTENCE WITH HIBERNATE
http://jpwh.org
Get the book, training, and consulting for your Hibernate team.


Top
 Profile  
 
 Post subject:
PostPosted: Sat May 05, 2007 12:07 pm 
Newbie

Joined: Thu May 03, 2007 6:24 am
Posts: 8
Wow - almost there ... thanks for the code snipped.
At least the positions are now inserted, although they are wrong:

The index numbers are completely shuffled ?
I first add child1, then child2 and child3.
Child1 got the position 3, child2 position 4 and child 3 position 2 within the database. See sql query at the end of this post...

WHAT is this ???

Here is all the code, so everyone is welcome to try this!
I hope we can get this very simple example case running properly. THANKS!

Code:
package model;

import javax.persistence.*;
import java.util.List;
import java.util.ArrayList;

@Entity
@Table(name = "Parent")
public class Parent {
    @Id
    private long id;
    @OneToMany(fetch = FetchType.LAZY)
    @JoinColumn(name = "parent_id", nullable = false)
    @org.hibernate.annotations.IndexColumn(name = "child_position")
    private List<Child> children = new ArrayList<Child>();

    public long getId() {
        return id;
    }

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

    public void addChild(Child child) {
        children.add(child);
    }

    public List<Child> getChildren() {
        return children;
    }

    public void setChildren(List<Child> children) {
        this.children = children;
    }
}

Code:
package model;

import javax.persistence.*;

@Entity
@Table(name = "Child")
public class Child {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;
    @ManyToOne
    @JoinColumn(name = "parent_id", insertable = false, updatable = false, nullable = false)
    private Parent parent;
    @Column(name = "child_position", insertable = false, updatable = false)
    private int position;

    public long getId() {
        return id;
    }

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

    public Parent getParent() {
        return parent;
    }

    public void setParent(Parent parent) {
        this.parent = parent;
    }

    public int getPosition() {
        return position;
    }
}

Code:
HibernateUtil.currentSession().getTransaction().begin();
model.Parent p = new model.Parent();
HibernateUtil.currentSession().save(p);

Child c1 = new Child();
c1.setId(0);
c1.setParent(p);
p.addChild(c1);
HibernateUtil.currentSession().save(c1);

Child c2 = new Child();
c2.setId(1);
c2.setParent(p);
p.addChild(c2);
HibernateUtil.currentSession().save(c2);

Child c3 = new Child();
c3.setId(2);
c3.setParent(p);
p.addChild(c3);
HibernateUtil.currentSession().save(c3);

p.addChild(c1);
p.addChild(c2);
HibernateUtil.currentSession().getTransaction().commit();
HibernateUtil.closeSession();

c2 = (Child)HibernateUtil.currentSession().createQuery("select child from Child child where child.id = 2").uniqueResult();
System.out.println("c2 = " + c2 + " " + c2.getPosition());

Code:
mysql> select * from Parent;
+----+
| id |
+----+
| 0  |
+----+
1 row in set (0.01 sec)

mysql> select * from Child;
+----+----------------+-----------+
| id | child_position | parent_id |
+----+----------------+-----------+
| 1  | 3              | 0         |
| 2  | 4              | 0         |
| 3  | 2              | 0         |
+----+----------------+-----------+
3 rows in set (0.01 sec)


Top
 Profile  
 
 Post subject:
PostPosted: Sat May 05, 2007 1:17 pm 
Newbie

Joined: Thu May 03, 2007 6:24 am
Posts: 8
SOLVED

Thanks Christian! Your snipped helped me to get it all working! Thanks for your support. Your credits++;

The last thing in my last post was MY FAULT!

Afer instantiating all the Objects and adding the children to the parent and saving the last Child c3, I had these two lines ...

Code:
p.addChild(c1);
p.addChild(c2);

By doing so, I was adding and c1, c2 AGAIN to the parent, which violates the OneToMany and ManyToOne relation. Hence, Hibernate was substituting the original c1 and c2, but incremented the position ;)
This would have only worked with a ManyToMany.

Anyway, after removing these two stupid lines in my code, it works:

Code:
mysql> select * from Child;
+----+----------------+-----------+
| id | child_position | parent_id |
+----+----------------+-----------+
| 1  | 0              | 0         |
| 2  | 1              | 0         |
| 3  | 2              | 0         |
+----+----------------+-----------+
3 rows in set (0.00 sec)

THANKS!

What remains for me know: Homework!
The mappedBy did not work for me, but the JoinColumn does ... need some more reading I suppose ...


Top
 Profile  
 
 Post subject: Re: [solved] @ManyToOne and @OneToMany; index position of list ?
PostPosted: Mon Apr 12, 2010 5:02 am 
Newbie

Joined: Sun Apr 02, 2006 4:33 pm
Posts: 7
As a side note, this solution is, at the time of my writing, not proper for people willing to cache this list.

More info of this unresolved 2007 bug: 2nd level cache broken for non-inverse bidirectional one-to-many relation. This issue was also discussed there: @ManyToOne field null when 2nd level cache enabled


Top
 Profile  
 
 Post subject: Re: [solved] @ManyToOne and @OneToMany; index position of list ?
PostPosted: Sat Apr 24, 2010 9:02 pm 
Beginner
Beginner

Joined: Sat Sep 12, 2009 10:20 am
Posts: 44
I started with using a non-inverse mapping so that I could get my sorting to work,
but now I am running into an issue with hibernate complaining about the backRef being null.

I think this is because I have a ManyToMany point to my Child from another object.
If I remove the nullable=false from my non-inverse mapping, then I nolonger get the backRef is null error, but I also don't get the inverse mapping.....


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 8 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.