-->
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.  [ 9 posts ] 
Author Message
 Post subject: Multiple association relationship between two entities
PostPosted: Thu Mar 05, 2009 4:22 pm 
Newbie

Joined: Thu Mar 05, 2009 3:56 pm
Posts: 10
I have two entities Item, User
Code:
class User
{
   @OneToMany(mappedBy="uploadedBy", cascade=CascadeType.ALL)
   List<Item> uploadedITems;

   @ManyToMany
   List<Item> bookmarkedItems;
}

class ITem
{
   @ManyToOne
   User uploadedBy;
}

OneToMany bidirectional relationship between User And Item through User.uploadedItems & Item.uploadedBy. A user can upload multiple Items. If a user is deleted, his uploaded Items should also be deleted

ManyToMany unidirectional relationship between User And Item through User.bookmarkedItems. A user can bookmark multiple Items uploaded by some other user.

Problem is:
Action=Delete User

Expected Behavior:User is deleted along with Items uploaded by him. If deleted Item is bookmarked by some other user, that user's bookmarkedList should be changed and that ITem should be removed from that List.

Behavior happening:On deletion of cascaded Item, when item is to be deleted, an exception occurs "Deletion of Item failed in table USER_ITEM as Foreign key of item is still referred in the table" because deleted ITem is bookmarked by some other user and that other user's bookmarked list is not altered hence Data Integrity Violation.

I think I am missing some annotation to let that thing happen.

Can you guys please help me with this issue? How this can be corrected?

One more thing is, in case I don't want cascade deletion to happen, same kind of error will be thrown because some uploadedItem will be still referring deleted USer through foreign key. How to correct this scenario also? @NotFound(action=IGNORE) is the answer?


Top
 Profile  
 
 Post subject:
PostPosted: Fri Mar 06, 2009 7:30 am 
Expert
Expert

Joined: Fri Jan 30, 2009 1:47 am
Posts: 292
Location: Bangalore, India
You give a MTM mapping from Item to User also. So this will make the hibernate delete the ITEM_USER table rows when an Item is deleted.

Like:
Code:
public class User {

   @ManyToMany
   @JoinTable(
         name="ITEM_USER",
         joinColumns={@JoinColumn(name="USER_ID")},
         inverseJoinColumns={@JoinColumn(name="ITEM_ID")})
   private List<Item> bookmarkedItems;
}


Similarly give mapping from Item to User:
Code:
public class Item {

   @ManyToMany
   @JoinTable(
         name="ITEM_USER",
         joinColumns={@JoinColumn(name="ITEM_ID")},
         inverseJoinColumns={@JoinColumn(name="USER_ID")})
   List<User> bookMarkedBy;
}

_________________
Regards,
Litty Preeth


Top
 Profile  
 
 Post subject:
PostPosted: Fri Mar 06, 2009 8:14 pm 
Newbie

Joined: Thu Mar 05, 2009 3:56 pm
Posts: 10
Thanks for the help.

But using the above prescribed annotation, On bookmarking an item by a user, I am getting two rows of same entries in the table ITEM_USER

ITEM_ID | USER_ID
------------------------
1 | 1
1 | 1




for User with id=1 bookmarking ITEM id=1


Top
 Profile  
 
 Post subject:
PostPosted: Sat Mar 07, 2009 10:39 am 
Expert
Expert

Joined: Fri Jan 30, 2009 1:47 am
Posts: 292
Location: Bangalore, India
Try using mappedBy also on one of the manytomany.

_________________
Regards,
Litty Preeth


Top
 Profile  
 
 Post subject:
PostPosted: Sat Mar 07, 2009 12:18 pm 
Newbie

Joined: Thu Mar 05, 2009 3:56 pm
Posts: 10
Yeah, I did that too.
so new code looks like
Code:
public class User {

   @ManyToMany
   @JoinTable(
         name="ITEM_USER",
         joinColumns={@JoinColumn(name="USER_ID")},
         inverseJoinColumns={@JoinColumn(name="ITEM_ID")})
   private List<Item> bookmarkedItems;
}

public class Item {

   @ManyToMany(mappedBy="bookmarkedItems")
   @JoinTable(
         name="ITEM_USER",
         joinColumns={@JoinColumn(name="ITEM_ID")},
         inverseJoinColumns={@JoinColumn(name="USER_ID")})
   List<User> bookMarkedBy;
}


But after this, deletion of a Item is resulting in same Data Integrity violation but deletion of a User happens correctly. Viceversa, if I move mapppedBy property from Item to User.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Mar 09, 2009 5:48 am 
Expert
Expert

Joined: Fri Jan 30, 2009 1:47 am
Posts: 292
Location: Bangalore, India
Hey I forgot something basic. For a bi-directional relation you should update both ends of the relation. The hibernate reference doc says (http://www.hibernate.org/hib_docs/v3/reference/en/html/tutorial-associations.html#tutorial-associations-usingbidir):
Hibernate reference tutorial wrote:
This "setting the link on both sides" is absolutely necessary and you should never forget doing it.


In your case, there is a cascade delete for the uploadedITems collection, so items will get deleted automatically. So you make item end of bookmark MTM relation as main and user end as inverse. So when item delete happens the link table records will get deleted automatically. But as the user end is inverse the automatic deletion wont happen. So before deleting a user you will first have to remove all of his bookmarks. So the code will look like this:

Code:
public class Item {

   @ManyToMany
   @JoinTable(
         name="ITEM_USER",
         joinColumns={@JoinColumn(name="ITEM_ID")},
         inverseJoinColumns={@JoinColumn(name="USER_ID")})
   List<User> bookMarkedBy;
}

public class User {

   @ManyToMany(mappedBy = "bookMarkedBy")
   private List<Item> bookmarkedItems;

   public void removeAllBookMarks() {
      if (getBookmarkedItems() != null) {
         for (Item i : bookmarkedItems) {
            i.getBookMarkedBy().remove(this);
         }
         bookmarkedItems.clear();
      }
   }

   public void addToBookmarkedItems(Item it) {
      it.getBookMarkedBy().add(this);
      if(getBookmarkedItems() == null) {
         bookmarkedItems = new HashSet<Item>();
      }
      getBookmarkedItems().add(it);
   }
}

For delete:
      User user = (User) session.get(User.class, 1L);
      user.removeAllBookMarks();
      session.delete(user);

For adding bookmark to an item:
      User user = (User) session.get(User.class, 1L);
      Item i = (Item) session.load(Item.class, 2L);
      user.addToBookmarkedItems(i);
      session.saveOrUpdate(user);

_________________
Regards,
Litty Preeth


Top
 Profile  
 
 Post subject:
PostPosted: Mon Mar 09, 2009 9:00 am 
Newbie

Joined: Thu Mar 05, 2009 3:56 pm
Posts: 10
Thanks for the info. Till now, I thought there should be some way in hibernate to perform the expected operation byitself, without me iteration over the collection to perform removal and deletion my self. But I haven't found anything yet in this direction. So I think, the way u suggested, thats the way to go.

Thanks for your help.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Mar 09, 2009 10:45 am 
Newbie

Joined: Sat Mar 07, 2009 4:24 am
Posts: 10
Data integrity is usually handled completely on server side with foreign keys. The scenario you have is one of the usages of "ON DELETE CASCADE" clause (which you will probably apply twice: once on column "uploaderUserId" of table "items" and once on column "itemID" of table "bookmarks").

_________________
-- please rate helpful posts --

Best regards,
Boris Okunskiy.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Mar 09, 2009 12:54 pm 
Newbie

Joined: Thu Mar 05, 2009 3:56 pm
Posts: 10
Code:
public class User {

   @ManyToMany
   @JoinTable(
         name="ITEM_USER",
         joinColumns={@JoinColumn(name="USER_ID")},
         inverseJoinColumns={@JoinColumn(name="ITEM_ID")})
   private List<Item> bookmarkedItems;
}


public class Item {

   @ManyToMany
   @JoinTable(
         name="ITEM_USER",
         joinColumns={@JoinColumn(name="ITEM_ID")},
         inverseJoinColumns={@JoinColumn(name="USER_ID")})
   List<User> bookMarkedBy;
}

When I put CASCADE on User.bookmarkedITems, all the bookmarked ITems are deleted even from the ITEM table and when I put CASCADE on Item.bookmarkedBy, all the Users bookmarked that Item is deleted.

But what I want is the row from the join table to be deleted not the entity.

If I put the mappedBy("bookmarkedITems") on the ManyToMany clause of Item.bookmarkedBy, deletion of a User is successfully done and relevant rows from the JOIN TABLE is deleted but on deletion of an ITEM, data integrity violation is raised because of mappedBy clause on Item.bookmarkedBy, relevant tuples from join table are not deleted.

And as I earlier explained. If I don't use mappedBy, I have duplicate tuples in the JOIN TABLE, though deletion happens perfectly, except that I get double the entries. :(

If this problem can still be solved with just annotation and without me writing a code manually to delete the ITems or USer from the List, I will be really great if someone can tell me the solution for that.

Thanks!


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