-->
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: Cascade problem when managing many-to-many
PostPosted: Wed Oct 22, 2008 11:35 am 
Newbie

Joined: Thu Oct 09, 2008 5:15 pm
Posts: 6
Due to various ridiculous legacy constraints, I have an odd requirement. I have to manually manage a many-to-many relationship and also manually assign the primary key of both sides of the relationship. I thought I had this set up properly, but I'm getting cascade problems.

I have two entities - E1 and E2. There is a many-to-many relationship between these two. I have to apply some attributes to the relationship between these two entities, though, so I'm manually managing the join table between them. Some code to illustrate:

Code:
public class E1 {
   Integer id;
   String name;
   Set<E1E2> joins;
   
   @OneToMany( mappedBy="e1" cascade = CascadeType.ALL )
   public Set<E1E2> getJoins() {
      return joins;
   }

   ...
}

public class E1E2 {
   Integer id;
   E1 e1;
   E2 e2;
   String someOtherAttribute;

   @ManyToOne()
   @JoinColumn( name = "E1id" )
   public E1 getE1() {
      return e1;
   }

   @ManyToOne()
   @JoinColumn( name = "E2id" )
   public E2 getE2() {
      return e2;
   }

   ...
}

public class E2 {
   Integer id;
   String name;
   ...
}



Because of some other requirements, I also have to set the ids of both E1 and E2. So, my code would end up looking something like this:

Code:
E1 entity1 = new E1();
entity1.setId(123);

E2 entity2 = new E2();
entity2.setId(456);

E1E2 join = new E1E2();
join.setE1(entity1);
join.setE2(entity2);

entity1.getJoins().add(join);

getHibernateTemplate().save(entity1);


I'd like the save of entity1 to cascade down through E1E2 and E2, but this isn't working right. The SQL generated ends up like this (paraphrased of course):

insert into E1(id) values (123)
select * from E2 where id=456 -- returns null because E2 doesn't exist yet
insert into E1E2 (E1id, E2id) values (123, null)

This of course triggers an exception because I don't allow nulls in the FK field.

I realize this is somewhat non-standard, but I don't know why Hibernate is replacing the E2 id I set with null.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Oct 22, 2008 1:19 pm 
Expert
Expert

Joined: Mon Nov 26, 2007 2:29 pm
Posts: 443
Your model is little confusing.
Is E1E2 an association table between E1 and E2?
If so, why do you have a Set<E1E2> as member of the E1 class, instead of just a Set<E2>?

_________________
Gonzalo Díaz


Top
 Profile  
 
 Post subject:
PostPosted: Wed Oct 22, 2008 1:32 pm 
Expert
Expert

Joined: Wed Mar 03, 2004 6:35 am
Posts: 1240
Location: Lund, Sweden
Does it help to add a Cascade.ALL to the E1E2.getE2() method? Though I don't know if Hibernate is smart enough to rearrange the order of the inserts so that it inserts E1, E2 and then E1E2.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Oct 22, 2008 1:38 pm 
Newbie

Joined: Thu Oct 09, 2008 5:15 pm
Posts: 6
nordborg, I actually do have Cascade set up on E1E2.getE2(), but I forgot to include it in my post. The updated class definition:

Code:
public class E1E2 {
   Integer id;
   E1 e1;
   E2 e2;
   String someOtherAttribute;

   @ManyToOne()
   @JoinColumn( name = "E1id" )
   public E1 getE1() {
      return e1;
   }

   @ManyToOne()
   @JoinColumn( name = "E2id" )
   @Cascade( org.hibernate.annotations.CascadeType.ALL )
   public E2 getE2() {
      return e2;
   }

   ...
}


Gonzalo, yes E1E2 is an association table. It's a part of E1 because E1E2 isn't just an association table, it also has to track some attributes.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Oct 22, 2008 1:54 pm 
Expert
Expert

Joined: Wed Mar 03, 2004 6:35 am
Posts: 1240
Location: Lund, Sweden
Ok, what about making the E1.getJoins() association inverse? The setup has some similarities with the example at http://www.hibernate.org/hib_docs/v3/re ... bidir.html


Top
 Profile  
 
 Post subject:
PostPosted: Wed Oct 22, 2008 3:08 pm 
Newbie

Joined: Thu Oct 09, 2008 5:15 pm
Posts: 6
I think you're right about my situation being similar to the example you linked. If I allow E2id to be nullable, it gets a lot further and runs this SQL:

Code:
Hibernate: insert into E1(Id) values (123)
Hibernate: select * from E2 where id=456
Hibernate: insert into E1E2 (E1id, E2id) values (123, null)
Hibernate: insert into E2 (id) values (456)
Hibernate: update E1EntityJoin set E2id=456, ProjectId=123 where id=1


This is very similar to that example. I don't know, though, how to make an association inverse using annotations.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Oct 22, 2008 3:49 pm 
Expert
Expert

Joined: Wed Mar 03, 2004 6:35 am
Posts: 1240
Location: Lund, Sweden
Ah... as it turns out I am not very familiar with using Hibernate annotations. The mappedBy attribute in the @OneToMany is actually what makes the collection an inverse collection.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Oct 22, 2008 4:22 pm 
Newbie

Joined: Thu Oct 09, 2008 5:15 pm
Posts: 6
The relationship from E2 to E1E2 is a @OneToMany so I can use mappedBy on that one (in fact it's already set this way) but doesn't that indicate that E1E2 owns the relationship? And isn't the goal to indicate that E2 owns it?


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.