Spring JPA OneToOne FK в виде каскада ПК. Удаление - PullRequest
0 голосов
/ 19 января 2019

У меня есть две таблицы, b и a:

  • они имеют двустороннее отношение "один к одному"
  • a имеет внешний ключb, который определяет это отношение
  • этот внешний ключ также рассматривается как первичный ключ для a, а JPA @ID
  • Я хочу каскадное удаление, которое удаляет связанныеb когда a удалено
  • в MySQL, a b_id равно NOT NULL

Проблема заключается в том, что при удалении моего Aобъект с JPA-хранилищем, я получаю ConstraintViolationException на его внешний ключ.Я ожидаю, что строки a и b будут удалены (умно, начиная с единицы a).

Как мне обойти это, зная, что я хочу сохранить:

  • моя схема БД такая же
  • каскадное удаление от a до b
  • идентификатор b является JPA @Id для a
CREATE TABLE `b` (
  `dbid` int(11) NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`dbid`),
);

CREATE TABLE `a` (
  `b_id` int(11) NOT NULL,
  KEY `b_fk` (`b_id`),
  CONSTRAINT `b_fk` FOREIGN KEY (`b_id`) REFERENCES `b` (`dbid`),
);

@Entity
@Table(name = "a")
public class A {

    @Id
    @Column(name = "b_id")
    @GeneratedValue(generator = "gen")
    @GenericGenerator(name = "gen", strategy = "foreign", parameters = @Parameter(name="property", value="b"))
    private Integer bId;

    @OneToOne(cascade = CascadeType.REMOVE)
    @PrimaryKeyJoinColumn
    private B b;
}
@Entity
@Table(name = "b")
public class B {

    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    @Column(name = "dbid")
    private Integer id;

    @OneToOne(mappedBy = "b")
    private A a;
}

[ПРАВИТЬ] После всех обсуждений в комментариях и перечитывания моего вопроса предложения с orphanRemoval действительно находятся в объеме и работают.

Ответы [ 5 ]

0 голосов
/ 30 января 2019

С точки зрения только стороны MySQL вашей реализации записи в таблице B не имеют «знания» о какой-либо записи в таблице A. В базе данных отношение является однонаправленным

Встроенная каскадная функциональность существует для предотвращения ошибок внешнего ключа, указав БД, что делать при удалении записи, внешний ключ не будет указывать никуда.Удаление таблицы A не вызовет ошибку внешнего ключа в любой записи таблицы B, поэтому любые функции собственного каскада не сработают

Повторно; Вы не можете сохранить схему такой же, и каскадное удаление с a до b, потому что у вас нет каскадного удаления с a до b

Вы также упомянули в комментариях, что некоторые записи таблицы B могут существовать без записей таблицы A, чего нет в исходном вопросе

Чтобы получить автоматическое удаление описанных вами записей таблицы B, у вас есть несколько вариантовпо отношению к БД:

  1. Поменяйте отношение на - Удалите текущий внешний ключ и добавьте столбец внешнего ключа, который может иметь значение NULL, в таблице B, который ссылается на первичный ключ таблицы A.Затем вы можете поместить каскадное удаление в этот внешний ключ.Оставьте новый столбец пустым для записей таблицы B, которые не принадлежат записи таблицы A.Вы также можете добавить уникальный индекс в этот столбец для защиты отношения один к одному
  2. Добавить триггер БД - При удалении записи таблицы A добавьте триггер БД, который удаляет ссылкузапись таблицы B
  3. Добавление процедуры БД - Добавить процедуру, которая удаляет запись таблицы A, а затем по очереди ссылку на запись таблицы B, возможно, в рамках транзакции.В дальнейшем удаляем только записи таблицы А, используя процедуру
  4. Не решайте проблему на уровне БД - По сути, то же, что и в варианте 3, но перемещайте логику процедуры из БД.слой в логику приложения

В JPA может быть что-то, что решает вашу дилемму из коробки, но под капотом это будет делать одно из перечисленных выше (не вариант 1 и, вероятно, вариант 4)

0 голосов
/ 30 января 2019

Чтобы добиться того, о чем вы просили, я настроил ваши таблицы следующим образом:

    CREATE TABLE b (
       dbid INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY
    );

    CREATE TABLE a ( 
       b_id int(11) NOT NULL PRIMARY KEY REFERENCES b(dbid) ON DELETE CASCADE
    );

CASCADE DELETE не был добавлен в ваш DDL.

Это включит каскадное удаление. Чтобы удалить запись b при удалении a, я сделал следующие изменения в классе A:

@Entity
@Table(name = "a")
public class A {

    @Id
    @Column(name = "b_id")
    @GeneratedValue(generator = "gen")
    @GenericGenerator(name = "gen", strategy = "foreign", parameters = @Parameter(name="property", value="b"))
    private Integer bId;

    @OneToOne(cascade = CascadeType.REMOVE, orphanRemoval = true)
    @PrimaryKeyJoinColumn
    private B b;
}

Это сработало хорошо. Я надеюсь, что это помогает.

Найдите здесь ссылку на рабочее решение .

0 голосов
/ 29 января 2019

попробуйте с кодом ниже -

@OneToOne(mappedBy = "b",cascade = CascadeType.ALL,fetch = FetchType.LAZY,orphanRemoval=true )
private A a;
0 голосов
/ 29 января 2019

Если вы хотите удалить объект B, всякий раз, когда удаляется связанный A (это четвертая точка вашего списка желаний:

Я хочу каскадное удаление, которое удаляет связанные b при удалении a

тогда вам нужно изменить отображение в A на:

@OneToOne(cascade = CascadeType.REMOVE, orphanRemoval = true)
@PrimaryKeyJoinColumn
private B b;
0 голосов
/ 23 января 2019

Можете ли вы попробовать в классе B добавить следующее

@OneToOne(mappedBy = "b", cascade = CascadeType.REMOVE)
private A a;

Кроме того, если в базе данных у вас есть только внешний ключ «a имеет внешний ключ к b», можете ли вы также сделать внешний ключ от b к a.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...