JPA @ManyToMany - Не удается удалить или обновить родительскую строку: ограничение внешнего ключа не удается

У меня есть сущности:

@Entity
public class User {

    @ManyToMany(cascade=CascadeType.PERSIST, fetch=FetchType.EAGER)
    private List<Role> roles = new ArrayList<Role>();

@Entity
public class Role {

    @ManyToMany(cascade=CascadeType.PERSIST, fetch=FetchType.EAGER)
    private Set<Permission> permissions = new HashSet<Permission>();

При выполнении удаления/удаления создается следующее исключение:

Caused by: com.mysql.jdbc.exceptions.MySQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`user_role`, CONSTRAINT `FK_USER_ROLE_roles_ID` FOREIGN KEY (`roles_ID`) REFERENCES `role` (`ID`))

Кажется, что есть проблема с сгенерированной таблицей соединений и внешними ключами.

Как это можно устранить, чтобы роль могла быть удалена?


Edit:

Экспортированный SQL показывает это:

CREATE TABLE IF NOT EXISTS `user_role` (
  `User_ID` bigint(20) NOT NULL,
  `roles_ID` bigint(20) NOT NULL,
  PRIMARY KEY (`User_ID`,`roles_ID`),
  KEY `FK_USER_ROLE_roles_ID` (`roles_ID`)
)

Ответ 1

Подумайте, как JPA разрешит отношения "многие ко многим".

Я предполагаю, что он создает таблицу User, таблицу Role и таблицу user_role, которая содержит ссылки (внешние ключи) для пользователя и для роли.

Теперь, если вы хотите удалить роль, вам нужно удалить все ссылки этой роли, которые хранятся пользователями. Для этого вам нужно перебрать всех пользователей, которые имеют такую ​​роль, и удалить их из этого списка ролей пользователя. Затем вы можете безопасно удалить роль.

Кстати, как только вы решите эту проблему, у вас, вероятно, будет следующая с Permission. Итак, если бы я был вами, я бы временно удалил поле permissions из Role, чтобы сделать работу с ролью, а затем восстановить permissions только для единственных новых проблем, если они существуют.

Ответ 2

Попробуйте добавить CascadeType.REMOVE к вашим сопоставлениям:

@ManyToMany(cascade= {CascadeType.PERSIST, CascadeType.REMOVE}, fetch=FetchType.EAGER)
private List<Role> roles = new ArrayList<Role>();

@ManyToMany(cascade= {CascadeType.PERSIST, CascadeType.REMOVE}, fetch=FetchType.EAGER)
private Set<Permission> permissions = new HashSet<Permission>();

Таким образом, дочерние объекты не должны удаляться перед родителем, поэтому вы можете удалить роль, не удаляя ранее ее разрешения.

Ответ 3

Я исправил это,

изменение некоторых имен таблиц (может быть, эти имена были зарезервированными словами в MySQL?)

Например: "администраторы" вместо "администратор"

@Table(name = "admins")
public class Admin extends TrackedEntity {

}

и путем изменения:

spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect

за:

spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect

в приложении. свойства