File APIs for Java Developers
Manipulate DOC, XLS, PPT, PDF and many others from your application.
http://aspose.com/file-tools
The moose likes Object Relational Mapping and the fly likes unwanted cascade delete behaviour Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login
JavaRanch » Java Forums » Databases » Object Relational Mapping
Bookmark "unwanted cascade delete behaviour" Watch "unwanted cascade delete behaviour" New topic
Author

unwanted cascade delete behaviour

Alan Shiers
Ranch Hand

Joined: Sep 24, 2003
Posts: 237
Hi there,

I have a dilemma I'm trying to solve. I'll start by descibing a part of my small application.
I have a hierarchy of classes like so:

abstract User
|
|__ Administrator extends User
|__ Instructor extends User
|__ Student extends User

In addition, I have a class named ScheduledCourse. It looks like so with a collection of instructors:

As you can see, there is an interim Table being created here that contains pointers to both the ScheduledCourse
and the instructor. This is named: SCHEDULED_INSTRUCTORS.
I'm not even sure the relationship should be OneToMany or ManyToMany. The logic is this: A ScheduledCourse
can have more than one instructor teaching the course. By the same token, an instructor can teach more
than one ScheduledCourse.
Therefore, the instructor class also has a collection of type ScheduledCourse. So, what is this really?
OneToMany or ManyToMany? I'm inclined to think it's the latter, isn't it?
The instructor class is the opposite end of this relationship with a collection of ScheduledCourses:

What I want to do is test the EntityManager.remove(Object o) method and its cascading affect on an instance of ScheduledCourse. Assume the collection of instructors in ScheduledCourse has only one instructor in it. This is my test code:

EntityManager em4 = emf.createEntityManager();
EntityTransaction tx4 = em4.getTransaction();
tx4.begin();
ScheduledCourse sc = em2.find(ScheduledCourse.class, new Long(1));
em4.remove(sc);
tx4.commit();
em4.close();

When hibernate starts the deletions it certainly clears the SCHEDULED_INSTRUCTORS table, which is what I want, but it doesn't stop there! This is not good. It actually continues a cascade deletion of the instructor from the INSTRUCTORS table and USERS table. I don't want it to do that! Could someone please explain what it is I need to do to get the appropriate behaviour I want?

Here's what I'm getting from my SQL log from the database:

SET AUTOCOMMIT TRUE
/*C19*/SET AUTOCOMMIT FALSE
DELETE FROM SCHEDULED_INSTRUCTORS WHERE SCHEDULED_COURSE_ID=1 AND USER_ID=3 //fine
DELETE FROM INSTRUCTORS WHERE USER_ID=3 //not good
DELETE FROM USERS WHERE USER_ID=3 //not good
DELETE FROM SCHEDULED_COURSES WHERE SCHEDULED_COURSE_ID=1 //fine
COMMIT
SET AUTOCOMMIT TRUE
/*C20*/SET AUTOCOMMIT FALSE

Please advise,

Alan
Mark Spritzler
ranger
Sheriff

Joined: Feb 05, 2001
Posts: 17250
    
    6

And what happens when you remove the hibernate delete-orphan cascade option? and then from there what happens when you remove the PERSIST cascade option. Because you have your mapping with the join table, which is good, it should automatically remove records from the join table and not affect the Instructors.

Mark


Perfect World Programming, LLC - Two Laptop Bag - Tube Organizer
How to Ask Questions the Smart Way FAQ
Alan Shiers
Ranch Hand

Joined: Sep 24, 2003
Posts: 237
Hi Mark,

I tried your suggestion. First I commented out the

@org.hibernate.annotations.Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)

line. And ran the app. No change.
Then I removed the CascadeType.PERSIST option and ran the app again. Still no change.

When I removed everything regarding cascading all together, it worked! Only the record in the SCHEDULED_INSTRUCTORS table was deleted. Which is what I wanted. So, the annotation really needed to just be:

@OneToMany
@org.hibernate.annotations.CollectionOfElements
@JoinTable(name = "SCHEDULED_INSTRUCTORS", joinColumns = {@JoinColumn(name = "SCHEDULED_COURSE_ID")}, inverseJoinColumns ={@JoinColumn(name = "USER_ID")})
private Collection<Instructor> INSTRUCTORS_SCHEDULED = new ArrayList<Instructor>();

I thought the cascading options had to be there in order to affect the SCHEDULED_INSTRUCTORS table, but I guess not. I guess hibernate is smart enought to know to only affect the one table and no others?

By the way, is this a OneToMany or ManyToMany relationship?

Alan

[ February 14, 2007: Message edited by: Alan Shiers ]

[ February 14, 2007: Message edited by: Alan Shiers ]
[ February 14, 2007: Message edited by: Alan Shiers ]
Mark Spritzler
ranger
Sheriff

Joined: Feb 05, 2001
Posts: 17250
    
    6

Originally posted by Alan Shiers:

By the way, is this a OneToMany or ManyToMany relationship?


Yes.

The important thing to Hibernate is that it is a ???ToMany and you have a Collection.

Hibernate is good in that it is a join table and hides it from your object model. However, in a lot of cases that join table will store extra information and you might want a Java Object to map to that join table and then your cascade options will be important.

Mark
Alan Shiers
Ranch Hand

Joined: Sep 24, 2003
Posts: 237
I'm glad you brought that up...
In the same class, ScheduledCourse, I have another collection of Enroll objects. The Enroll class represents a Join Table as well which contains additional fields. I used the example as presented in the book Java Persistence with Hibernate Chapter 7 page 304, regarding "Mapping the join table to an intermediate entity".

Again, I'm trying to test the delete feature by running my test app to see what affect hibernate has on the ScheduledCourse instance which also includes this collection of Enroll objects, like so:



The Student class has the same at its end:



The Enroll class looks like so:


So, imagine if you will, that I've enrolled three students into the ScheduledCourse instance. This means the Enroll table has these three records in it tying in the students with the ScheduledCourse.

Once again, when I call this in my test app:

EntityManager em = ...;
ScheduledCourse sc = em.find(ScheduledCourse.class, new Long(1));
em.remove(sc);

Naturally, one would expect that those records in the Enroll table to disappear. But they don't. I tried eliminating the cascading options altogether, as I did for the collection of Instructors, anything I do has no affect.

Can you explain what needs to be done in this scenario to make it work?

Alan
 
I agree. Here's the link: http://aspose.com/file-tools
 
subject: unwanted cascade delete behaviour