Feng Xi
Feng Xi

Reputation: 1

hibernate @ManyToOne self reference (unidirectional) cascade delete parent

Using MYSQL, I have a self-reference Folder @Entity, which might has a parent Folder. The Entity defines as below:

@Entity
public class Folder {
   @Id
   private int id;
   private String name;

   @ManyToOne(cascade = {CascadeType.ALL})
   @JoinColumn(name = "parent_folder_id")
   protected Folder parentFolder;  
}

What I need is whenever I deleted a parent folder, all its sub-folders can be automatically deleted. but with above code, I always get error:

Folder sub1 = new Folder();
Folder sub2 = new FOlder();
Folder parent = new Parent();
sub1.setParent(parent);
sub2.setParent(parent);

session.save(sub1);// which can save parent automatically.
session.save(sub2);

But when I try to session.delete(parent)

Caused by: java.sql.BatchUpdateException: Cannot delete or update a parent row: a foreign key constraint fails

Please NOTE: I'd like to keep the unidirectional @ManyToOne here.

Upvotes: 1

Views: 1674

Answers (1)

Predrag Maric
Predrag Maric

Reputation: 24433

I don't know why you want to keep the unidirectional relation here, because making it bidirectional would make your life much easier.

First, CascadeType.ALL on the parent mapping in your example means that if you delete the subfolder, its parent will be deleted also. And this will propagate all the way up to the root folder which is probably not what you want.

With bidirectonal mapping it would look like this

@ManyToOne
@JoinColumn(name = "parent_folder_id")
protected Folder parentFolder;  

@OneToMany(mappedBy = "parentFolder", cascade = {CascadeType.ALL})
protected Set<Folder> children;  

Without this, you should implement the cascade yourself. This will get complicated for more complicated folder structures (when checking the children of a folder before deletion, you'll have to check grandchildren... and so on). It could be done with one recursive method, but definitely a lot more work than with bidirectional relation.

/* pseudo code */
public void deleteFolder(Folder f) {
    Set<Folder> children = session.createQuery("from Folder where parentFolder.id = :parentId").setParameter("parentId", f.getId()).list();
    for each child {
        deleteFolder(child)
    }
    session.delete(f)
}

Upvotes: 2

Related Questions