Search code examples
javahibernatejpamany-to-manycascade

@ManyToMany with cascade = CascadeType.REMOVE removes associations AND entities


I have 2 entities: Group and Grouped, with 1 ManyToMany association.

In database, the Association table has a NOT NULL FK on both Group and Grouped.

I want Hibernate to delete the association but not the group when all grouped are deleted.

Code to delete a Grouped entity:

@Autowired
private final GroupedRepository groupedRepository;

public void delete(Grouped groupedToRemove) {
    groupedRepository.delete(groupedToRemove);
}

If I set cascade = CascadeType.ALL or cascade = CascadeType.REMOVE, my Group entities are deleted when I delete a Grouped entity, not only the associations:

@ManyToMany(cascade = CascadeType.ALL, // same behavior with CascadeType.REMOVE
        mappedBy = "grouped", 
        targetEntity = Group.class)
private Set<Group> groups = new HashSet<>();

If I remove the cascade, hibernate tries to set group_id=null and it throws a ModelConstraintException. I don't want to set the FK as nullable.

Group entity:

@Entity
@Table(name = "groups")
@Getter
@Setter
public class Group {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    @ManyToMany(targetEntity = Grouped.class)
    @JoinTable(
            name = "association",
            joinColumns = @JoinColumn(name = "group_id", nullable = false, updatable = false),
            inverseJoinColumns = @JoinColumn(name = "grouped_id", nullable = false, updatable = false)
    )
    private Set<Grouped> grouped= new HashSet<>();
}

Grouped entity:

@Entity
@Table(name = "grouped")
@Getter
@Setter
public class Grouped {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    @ManyToMany(mappedBy = "grouped", targetEntity = Group.class)
    private Set<Group> groups= new HashSet<>();
}

Solution

  • That's the expected behavior. REMOVE cascading means: when removing this entity, also remove the associated entities. It makes no sense on a ManyToXxx, since obviously, other entities are still referencing the associated entity.

    If you want to delete a Grouped, but leave the associated Groups there, you need to remove the association between the two entities first:

    for (Group group : grouped.getGroups()) {
        group.getGrouped().remove(grouped);
    }
    grouped.getGroups().clear();
    

    and then remove the Grouped entity, which is not associated to any Group anymore.