-->
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.  [ 20 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: General JPA (Hibernate) question in the case of two relation
PostPosted: Sun Jan 27, 2008 5:39 pm 
Regular
Regular

Joined: Thu May 04, 2006 5:24 am
Posts: 55
Hi,

I have one general JPA (Hibernate) question regarding when one have the case with two classes and two relations between them. I supose that this will be interesting case for some people.

So, I have two classes with two relations between them, like this:

|Class1| <>-[1]--------[*]-> |Class2|

|Class1| ----------------[1]-> |Class2|

This is the actual code for these relations:

Class 1:

Code:
@javax.persistence.Entity
public class Class1 implements java.io.Serializable {

   private Long id;

   private java.util.Set<Class2> classes = new java.util.HashSet<Class2>();

   private Class2 class2;

   public Class1() {

   }

   @javax.persistence.Id
   @javax.persistence.GeneratedValue
   public Long getId() {
      return this.id;
   }   

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


   @javax.persistence.OneToMany(mappedBy="class1", cascade=javax.persistence.CascadeType.ALL)
   public java.util.Set<Class2> getClasses() {
      return this.classes;
   }

   public void setClasses(java.util.Set<Class2> classes) {
      this.classes= classes;
   }   

   public Class2 getClass2() {
      return this.class2;
   }

   public void setClass2(Class2 class2) {
      this.class2 = class2;
   }   
}


Class 2:

Code:
@javax.persistence.Entity
public class Class2 java.io.Serializable {

   private Long id;

   private Class1 class1;

   public Class2() {
   }   

   @javax.persistence.Id
   @javax.persistence.GeneratedValue
   public Long getId() {
      return this.id;
   }   

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

   @javax.persistence.ManyToOne
   public Class1 getClass1() {
      return this.class1;
   }

   public void setClass1(Class1 class1) {
      this.class1 = class1;
   }
}


Now, the test code:

Code:
Class2 c1 = new Class2();
Class2 c2 = new Class2();
Class2 c3 = new Class2();

Set<Class2> list = new LinkedHashSet<Class2>();
list.add(c1); list.add(c2); list.add(c3);

Class1 in = new Class1();

in.setClasses(list); <- this is related to the first diagram
in.setClass2(c2); <- this is related to the second diagram

hib.persist(in); <- THIS DOESN'T WORK, because Hibernate first try to insert [b]in[/b] object (Class1), and it doesn't have id of the class2.


But if I write this changed test code:

Code:
Class2 c1 = new Class2();
Class2 c2 = new Class2();
Class2 c3 = new Class2();

Set<Class2> list = new LinkedHashSet<Class2>();
list.add(c1); list.add(c2); list.add(c3);

Class1 in = new Class1();

// Now I manually add ref to Class1 object
c1.setClass1(in);
c2.setClass1(in);
c3.setClass1(in);

in.setClasses(list); <- this is related to the first diagram
in.setClass2(c2); <- this is related to the second diagram

hib.persist(in);  <- NOW IT WORKS!!! But why ?! It firstly writes Class1 objects, then Class2 objects and in the end it updates Class1 giving it reference to Class2.


The question is why this doesn't work in the first case when I don't manually add ref to Class1 object ?


Top
 Profile  
 
 Post subject: Re: General JPA (Hibernate) question in the case of two rela
PostPosted: Sun Jan 27, 2008 8:56 pm 
Expert
Expert

Joined: Wed Apr 11, 2007 11:39 am
Posts: 735
Location: Montreal, QC
First off, this is a little bit different from the original example but the explanation is that you have to set the pointer to Class1 in c1, c2, and c3 anyways because in a ManyToOne relationship the ManyToOne side owns the relation. After all, it is where in database that foreign key is kept. The second test passes because this relation is set in it and the ManyToOne side is set to optional (I think it is the default but it needs to be verified).

However, back to the example you had before where the ManyToOne side had a optional = false, HB would throw an exception anyways because in needs c2 to be saved before it is saved and c2 needs in to be saved before it is saved it confuses hibernate.


Farzad-


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jan 28, 2008 2:13 am 
Regular
Regular

Joined: Mon Aug 20, 2007 6:47 am
Posts: 74
Location: UK
Out of interest, what happens if you take the following line of code out of each of your test cases?

Code:
in.setClass2(c2); <- this is related to the second diagram


I would expect the test result to be unchanged in both instances, because you have not told hibernate about the |Class1| ----------------[1]-> |Class2| relationship.

When you load an instance of Class1 from a session, I would always expect getClass2() to return null.

Please correct me if I've made a mistake here.


Top
 Profile  
 
 Post subject: Re: General JPA (Hibernate) question in the case of two rela
PostPosted: Mon Jan 28, 2008 7:21 am 
Regular
Regular

Joined: Thu May 04, 2006 5:24 am
Posts: 55
farzad wrote:
First off, this is a little bit different from the original example but the explanation is that you have to set the pointer to Class1 in c1, c2, and c3 anyways because in a ManyToOne relationship the ManyToOne side owns the relation. After all, it is where in database that foreign key is kept. The second test passes because this relation is set in it and the ManyToOne side is set to optional (I think it is the default but it needs to be verified).

However, back to the example you had before where the ManyToOne side had a optional = false, HB would throw an exception anyways because in needs c2 to be saved before it is saved and c2 needs in to be saved before it is saved it confuses hibernate.


Farzad-


O.K. But, is there any solution for this that one don't need manually to add those references to Class1 in Class2, like OneToOne, or similar ?!


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jan 28, 2008 7:24 am 
Regular
Regular

Joined: Thu May 04, 2006 5:24 am
Posts: 55
Irons UK wrote:
Out of interest, what happens if you take the following line of code out of each of your test cases?

Code:
in.setClass2(c2); <- this is related to the second diagram


I would expect the test result to be unchanged in both instances, because you have not told hibernate about the |Class1| ----------------[1]-> |Class2| relationship.

When you load an instance of Class1 from a session, I would always expect getClass2() to return null.

Please correct me if I've made a mistake here.


Yes, you're right, when I remove that line nothing happens, i.e., if I manually don't add Class1 reference to Class2 objects, it fails.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jan 28, 2008 10:28 am 
Regular
Regular

Joined: Thu May 04, 2006 5:24 am
Posts: 55
One more thing, when I do this (removed additional Class2 reference in Class1):

Code:
Class2 c1 = new Class2();
Class2 c2 = new Class2();
Class2 c3 = new Class2();

Set<Class2> list = new LinkedHashSet<Class2>();
list.add(c1); list.add(c2); list.add(c3);

Class1 in = new Class1();

in.setClasses(list);

--start Transaction

hib.persist(in);

--end Transaction

--start Transaction

hib.remove(in); <- HERE I GET AN ERROR!!!!!

--end Transaction


I got following error in my test class:


Code:
15:27:19,671 ERROR org.hibernate.AssertionFailure:22 - an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session)
org.hibernate.AssertionFailure: null id in com.test.Class2 entry (don't flush the Session after an exception occurs)
   at org.hibernate.event.def.DefaultFlushEntityEventListener.checkId(DefaultFlushEntityEventListener.java:55)
   at org.hibernate.event.def.DefaultFlushEntityEventListener.getValues(DefaultFlushEntityEventListener.java:164)
   at org.hibernate.event.def.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:120)
   at org.hibernate.event.def.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:196)
   at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:76)
   at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:26)
   at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
   at org.hibernate.ejb.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:297)
at myClass...


Why I'm getting this error ?


Top
 Profile  
 
 Post subject: Re: General JPA (Hibernate) question in the case of two rela
PostPosted: Mon Jan 28, 2008 11:43 am 
Expert
Expert

Joined: Wed Apr 11, 2007 11:39 am
Posts: 735
Location: Montreal, QC
MilanMilanovich wrote:
O.K. But, is there any solution for this that one don't need manually to add those references to Class1 in Class2, like OneToOne, or similar ?!


I would give a different answer for each association type but you should be able to reason this if you think of the reverse tag hibernate has. It is equivalent to the mappedBy attribute in the JPA world. Each association has two sides, but there is only one side that owns the association. Owning an association mostly means who stores an association in database. After all, an association can not be stored in database twice because two classes are referring it. In some association types, like Many-To-One you have to have the Many-To-One side to own the association. In cases like Many-To-Many you may choose either side to have it. When you decide which end owns an association, you know where at least you need to set the references. In your example you really have to set the reference to Class1 in Class2 because Class2s own the relation to Class1s. Putting the values in the set in Class1 is really optional but that is usually useful in many senses.


Farzad-


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jan 28, 2008 11:47 am 
Expert
Expert

Joined: Wed Apr 11, 2007 11:39 am
Posts: 735
Location: Montreal, QC
Between --end Transaction and --start Transaction try to get reference to a new EntityManager.



Farzad-


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jan 28, 2008 11:49 am 
Regular
Regular

Joined: Thu May 04, 2006 5:24 am
Posts: 55
Dear Farzad,

Yes, but I put this in Class1:

Code:
@javax.persistence.OneToMany(mappedBy="class1",


as you can see for
Code:
getClasses()


so this relation should be mapped by Class1 ?

I noticed also when I persist Class1 instance like in the example above, Class2 instances didn't get reference to Class1 ?


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jan 28, 2008 11:54 am 
Expert
Expert

Joined: Wed Apr 11, 2007 11:39 am
Posts: 735
Location: Montreal, QC
MilanMilanovich wrote:
so this relation should be mapped by Class1


Negative. It means the relation is mappedBy class1 attribute of Class2. Think of database. Class2 has the foreign key of Class1. It makes sense that Class2 owns the relation.


Farzad=


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jan 28, 2008 11:55 am 
Regular
Regular

Joined: Thu May 04, 2006 5:24 am
Posts: 55
farzad wrote:
Between --end Transaction and --start Transaction try to get reference to a new EntityManager.



Farzad-


Why's that ? I have just one EntityManager in my Test class.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jan 28, 2008 11:59 am 
Expert
Expert

Joined: Wed Apr 11, 2007 11:39 am
Posts: 735
Location: Montreal, QC
MilanMilanovich wrote:
farzad wrote:
Between --end Transaction and --start Transaction try to get reference to a new EntityManager.



Farzad-


Why's that ? I have just one EntityManager in my Test class.


An EntityManager has a one to one relation to a session object and a session has internal memory, so basically it is not very safe to use a session in scope of two different transactions. I am not sure what the use cases are though. There might be situations in which this is fine. I also know JPA has an ExtendedEntityManager or something similar that is able to span two or more transactions. If you like you could take a look at that one.


Farzad-


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jan 28, 2008 11:59 am 
Regular
Regular

Joined: Thu May 04, 2006 5:24 am
Posts: 55
farzad wrote:
Between --end Transaction and --start Transaction try to get reference to a new EntityManager.



Farzad-


Why's that ? I have just one EntityManager in my Test class.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jan 28, 2008 12:04 pm 
Regular
Regular

Joined: Thu May 04, 2006 5:24 am
Posts: 55
farzad wrote:
MilanMilanovich wrote:
farzad wrote:
Between --end Transaction and --start Transaction try to get reference to a new EntityManager.



Farzad-


Why's that ? I have just one EntityManager in my Test class.


An EntityManager has a one to one relation to a session object and a session has internal memory, so basically it is not very safe to use a session in scope of two different transactions. I am not sure what the use cases are though. There might be situations in which this is fine. I also know JPA has an ExtendedEntityManager or something similar that is able to span two or more transactions. If you like you could take a look at that one.


Farzad-


Dear Farzad,

I don't use session in the scope of two transactions, I have one trascation, which I commit, then I again open transaction do the work and commit the second transaction.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jan 28, 2008 12:07 pm 
Expert
Expert

Joined: Wed Apr 11, 2007 11:39 am
Posts: 735
Location: Montreal, QC
MilanMilanovich wrote:
I don't use session in the scope of two transactions, I have one trascation, which I commit, then I again open transaction do the work and commit the second transaction.



That's two transactions. A transaction is done after commit.


Farzad-


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 20 posts ]  Go to page 1, 2  Next

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.