9

I have two normal tables and one relation table.

 --------------------------------------------------
|  Group         | Membership      | User          |
| (ID, NAME)     |(GRP_ID, U_ID)   |(ID, FORENAME) |
 --------------------------------------------------
|  1,  Admin     | 1,      1       | 1, Joe        |
 --------------------------------------------------

The membership has two Foreign Keys

FK1 Membership.GRP_ID -> Group.ID
FK2 Membership.U_ID   -> User.ID

I can set cascade-delete to each of the foreign keys (FK1 and FK2).


Fact 1

If neither FK1 nor FK2 does delete cascade, I can neither remove Admin nor Joe because they remain connected in "membership".

CREATE TABLE Membership (
    GRP_ID INT NOT NULL,
    U_ID INT NOT NULL,
    FOREIGN KEY (GRP_ID) REFERENCES Group (id),
    FOREIGN KEY (U_ID) REFERENCES User (id)
);

Fact 2

If FK1 cascade delete but FK2 not, you can delete Admin (what removes the membership) but you can not delete Joe.

CREATE TABLE Membership (
    GRP_ID INT NOT NULL,
    U_ID INT NOT NULL,
    FOREIGN KEY (GRP_ID) REFERENCES Group (id) ON DELETE CASCADE,
    FOREIGN KEY (U_ID) REFERENCES User (id) 
);

Fact 3

If FK2 cascade delete but FK1 not, you can delete Joe (what removes the membership) but you can not delete Admin.

CREATE TABLE Membership (
    GRP_ID INT NOT NULL,
    U_ID INT NOT NULL,
    FOREIGN KEY (GRP_ID) REFERENCES Group (id),
    FOREIGN KEY (U_ID) REFERENCES User (id) ON DELETE CASCADE
);

Fact 4

If FK2 cascade delete and FK1 cascade delete. Deletion of Joe will remove the Membership but Admin remains unchanged. Deletion of Admin will remove the Membership but Joe remains unchanged.

CREATE TABLE Membership (
    GRP_ID INT NOT NULL,
    U_ID INT NOT NULL,
    FOREIGN KEY (GRP_ID) REFERENCES Group (id) ON DELETE CASCADE,
    FOREIGN KEY (U_ID) REFERENCES User (id) ON DELETE CASCADE
);


Java Code of Group:

// @formatter:off
import javax.persistence.*;
import javax.persistence.Entity;
import javax.persistence.Table;
import java.io.Serializable;
import javax.persistence.ManyToOne;
import javax.persistence.JoinColumn;
import javax.persistence.Column;
import javax.persistence.Version;
import javax.persistence.SequenceGenerator;
import javax.persistence.Id;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import org.hibernate.envers.Audited;
import java.util.LinkedHashSet;
import javax.persistence.OneToMany;
import java.util.Set;
import javax.persistence.OrderBy;
import javax.persistence.ManyToMany;
import javax.persistence.JoinTable;

@Entity
@Table(name="Group")
@SuppressWarnings("serial")
public class Group implements java.io.Serializable {
  private Integer id;
  private Set<User> users = new LinkedHashSet<>();
  private String name;

  @Id
  @Column(name = "ID", nullable = false)
  @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "gen241738")
  @SequenceGenerator(name = "gen241738", sequenceName = "seq_group_id")
  public Integer getId() {
    return id;
  }
  public void setId(Integer id) {
    this.id = id;
  }

  @OrderBy
  @ManyToMany
  @JoinTable(name="Membership", joinColumns = {
    @JoinColumn(name="GRP_ID", referencedColumnName="ID", nullable=false),
  }, inverseJoinColumns = {
    @JoinColumn(name="U_ID", referencedColumnName="ID", nullable=false)
  })
  public Set<User> getUsers() {
    return users;
  }
  public void setUsers(Set<User> users) {
    this.users = users;
  }

  @Column(name = "NAME", nullable = false)
  public String getName() {
    return this.name;
  }
  public void setName(String name) {
    this.name = name;
  }
}
//@formatter:on


Question

When a relation table contains two foreign keys scaling the possibilities to 2x2=4 cases, why does the annotation @ManyToMany allows only one delete-cascadation?

frido
  • 13,065
  • 5
  • 42
  • 56
Grim
  • 1,938
  • 10
  • 56
  • 123
  • Can you explain with example code? which cascade used? – MostafaMashayekhi Jul 15 '19 at 07:13
  • @MostafaMashayekhi Sure, what language and database do you prefer? – Grim Jul 15 '19 at 12:08
  • This is clear because you use java and spring tag – MostafaMashayekhi Jul 15 '19 at 13:35
  • @MostafaMashayekhi You are right. What database do you prefer? – Grim Jul 15 '19 at 17:45
  • I usually use MySQL – MostafaMashayekhi Jul 15 '19 at 18:22
  • @MostafaMashayekhi Updated question to the requirements. – Grim Jul 16 '19 at 01:11
  • JPA Cascade Types CascadeType.PERSIST CascadeType.MERGE CascadeType.REFRESH CascadeType.REMOVE CascadeType.DETACH CascadeType.ALL –  Jul 17 '19 at 06:06
  • @AnvarQuvandiqov REMOVE naturally – Grim Jul 17 '19 at 06:42
  • @PeterRader the *@ManyToMany* annotation is not the best option, did you try using *@ManyToOne* from both sides, and creating a POJO for the Membership class? – Villat Jul 17 '19 at 22:18
  • @Villat Why is it not the best option? On an many-to-many entity I have to delete 3 entitys instead of one. This leads us to more code, to more bug-sources. More code to care. Because noone understand the many-to-many developed by some good developers? Why is work of good developers that bad? – Grim Jul 18 '19 at 07:43
  • @PeterRader you’d probably need to delete 2 entities (the Membership and another one), no 3, because the entities are associated to other entities as well. Regarding the annotation itself, from my experience, is better to manage the relation table as a POJO – Villat Jul 18 '19 at 12:19
  • @Villat Having group-members-count as n, you have to delete 1+n*2. Your experience is to better delete 1+n*2 instead of 1? Design this as an answer but I am sure your answer will be get downvoted. Also you did not answer the question in my last comment. – Grim Jul 18 '19 at 13:17
  • Regarding the questions about developers, I don't think that the work is "that bad", the many-to-many covers some cases, but not all you need. About the second comment, if you want to delete a group, using my approach you're going to delete the group+membership, but not the user, so it isn't 1+n*2. – Villat Jul 18 '19 at 13:25
  • 1
    @PeterRader nevermind, I was trying to help, but your superb attitude is annoying. The experience isn't directly proportional to the use of Stackoverflow. Good luck finding help. – Villat Jul 18 '19 at 14:31
  • 1
    @Villat You are right, it is a idealistic attitude, I am sorry to steal your time. I changed the question to add the idealistic-tag. – Grim Jul 18 '19 at 17:38

2 Answers2

0

When a relationtable contains two foreign keys scaling the possibilitys to 2x2=4 cases, why does the annotation @ManyToMany allows only one delete-cascadation?

Cascading is defined not just by type but also by owning side of relationship:

Entities that use relationships often have dependencies on the existence of the other entity in the relationship. For example, a line item is part of an order; if the order is deleted, the line item also should be deleted. This is called a cascade delete relationship.

For @ManyToMany:

Every many-to-many association has two sides, the owning side and the non-owning, or inverse, side. The join table is specified on the owning side. If the association is bidirectional, either side may be designated as the owning side. If the relationship is bidirectional, the non-owning side must use the mappedBy element of the ManyToMany annotation to specify the relationship field or property of the owning side. The join table for the relationship, if not defaulted, is specified on the owning side.

So from your example, Group is owning side and I would suppose that User is non-owning side, so it is representing fact 2 and fact 3 vice versa.

@Entity
@Table(name="Group")
@SuppressWarnings("serial")
public class Group implements java.io.Serializable {

  @OrderBy
  @ManyToMany
  @JoinTable(name="Membership", joinColumns = {
    @JoinColumn(name="GRP_ID", referencedColumnName="ID", nullable=false),
  }, inverseJoinColumns = {
    @JoinColumn(name="U_ID", referencedColumnName="ID", nullable=false)
  })
  public Set<User> getUsers() {
    return users;
  }
}

If the association is bidirectional, either side may be designated as the owning side. If you do so, you get representation of your fact 4

For last example you should define POJO representation of your Mapping table as owning side of each relationship.

Im showing you that there is no missing configuration/field and all of cases could be done by specifing the owning side. You are wrongly expecting @ManyToMany's field cascade to do whole cascade configuration.


Just spotted in comment that you are really asking for explanation of cascading:

The answer did not help me in understanding what is the matter of cascade=null

With cascade you are specifing operations that must be cascaded to the target of the association. Cascading is performed by hibernate, its not db level cascade operation. With relationship defined as above, we are telling if User asociated with Group should also persist/merge/remove/refresh/detach when Group does (using appropriate type) or dont (using cascade=null).

Tijkijiki
  • 792
  • 7
  • 19
  • I just used your example to demonstrate how your definition of relationship behaves. – Tijkijiki Jul 22 '19 at 13:25
  • Again, I don't, you do. I thought that you did define cascade such way, because CascadeType.REMOVE would also remove `User` and this behaviour doesnt fit expected posibilities for cascading as you definied in your question. May I ask why did you downvoted my answer? – Tijkijiki Jul 22 '19 at 14:14
  • I edited my answer, I hope that I answered your question. – Tijkijiki Jul 22 '19 at 15:46
  • Ok, lets go through your question again. You specified 4 cases for cascade delete in relation table. I transfered those 4 cases to java/hibernate cases, showing you that there is **no missing configuration/field and all of them could be done by specifing the owning side**. You are wrongly expecting `@ManyToMany`'s field `cascade` to do whole cascade configuration and figuratively another answer. – Tijkijiki Jul 22 '19 at 20:38
  • Just edited my answer, since I'm only explain the context. – Tijkijiki Jul 22 '19 at 21:13
-1

In this instance, you don't want to delete another party when you delete Group or User.

@ManytoMany is to maintain the relationship between two parties. So if it designed to allow two delete-cascadation, it will:

  1. hard to understand
  2. lead to some situation which you don't want to see

The best way to maintain the relationship is to operate the relation table directly. After that you can delete/add Group table or User table as you want.

The annotation @ManytoMany, in my opinion, is designed to lead us operate the relation table. I don't know if I answerd your question. If I did, please let me know, thanks.

Grim
  • 1,938
  • 10
  • 56
  • 123
qk cong
  • 11
  • 3
  • I'm sorry that I didn't notice the wrong word I wrote. I want to write Group and User. The word operator is actually operate. – qk cong Jul 22 '19 at 01:42
  • and `(...)is designed to lead us operate the(...)` is actually *let us operate* ? – Grim Jul 22 '19 at 06:41