JPA не генерирует "on delete set null" Ограничения FK

У меня есть два связанных клана JPA-аннотированных. Тревога и состояние. Один сигнал тревоги может иметь один статус.

Мне нужно, чтобы иметь возможность удалять одно состояние и "распространять" нулевое значение в Alarms, которые находятся в этом статусе, который был удален.

То есть, мне нужно, чтобы внешний ключ определялся как " при удалении set null".

@Entity
public class Alarm {
    @Id
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="sequence")
    @SequenceGenerator(name="sequence", sequenceName="alarm_pk_seq")
    private Integer id;

    @OneToOne(cascade=CascadeType.ALL)
    @JoinColumn(name="idStatus")
    private Status status;

    // get/set
}

@Entity
public class Status {
    @Id
    @Column(name="idStatus")
    private Integer id;

    private String description;

    // get/set
}

Пример:

До:

STATUS
id  description
1   new
2   assigned
3   closed

ALARMS
id  status
1   1
2   2
3   2

После (удаление статуса с id = 2)

STATUS
id  description
1   new
3   closed

ALARMS
id  status
1   1
2   NULL
3   NULL

Я использую Hibernate и PostgreSQL, автоматически генерируя базу данных из исходного кода. Я пробовал с любым возможным CascadeType без успеха.

Что-то не так в коде? Можно ли это сделать с помощью JPA?

Ответ 1

Просто добавьте это, используя аннотацию Hibernate:

@OnDelete(action=OnDeleteAction.CASCADE)

генерирует внешний ключ как: "Вкл. ОБНОВЛЕНИЕ НЕТ ДЕЙСТВИЙ НА УДАЛЕНИЕ КАСКАДЫ";

Но нет действия = OnDeleteAction.SET_NULL

Кроме того, я не люблю привязывать свой код к Hibernate, если это возможно (но я могу жить с ним, если он работает).

Этот thread обсуждает это. Я не могу поверить, что в JPA (или Hibernate extensions) нет простого метода для генерации внешнего ключа.

Ответ 2

В вашем случае вы генерируете базу данных из классов. Это подразумевает, что вы не будете использовать базу данных для других целей (так как это заставит вас иметь DDL-скрипты). Это означает, что это правило, реализованное в базе данных или в Java-коде, не имеет значения.

Мы также знаем, что Hibernate повышал бы временное исключение в случае, если удалять один или несколько статусов и пытаться ссылаться на него при совершении без каскада.

Кроме того, база данных будет сгенерирована с использованием ограничения внешнего ключа.

Все это означает, что ограничение должно быть соблюдено, чтобы приложение работало.

Если ваши сущности находятся в банке сами по себе, вы можете добавить переходный метод к сигналу тревоги или интерфейсу состояния, чтобы удалить статус при соблюдении правила.

Кроме того, программисты при использовании объектов будут вынуждены соблюдать правило, иначе код не будет работать. Но чтобы облегчить задачу, вы можете сделать соотношение двунаправленным, чтобы облегчить задачу отслеживания аварийных сигналов из статусов.

Если вы можете, используйте ondelete interceptor/listener, чтобы установить для свойства alarm.status значение null.

Ответ 4

Вы уверены, что используете @OneToOne? Мне кажется, что лучше использовать @ManyToOne (поскольку состояние может быть затронуто несколькими тревогами):

@Entity
public class Alarm {
    ...

    @ManyToOne(cascade=CascadeType.ALL)
    @JoinColumn(name="idStatus", nullable=true)
    private Status status;

    ...
}

Ответ 5

OpenJPA имеет

@ForeignKey(deleteAction=ForeignKeyAction.NULL)

но нет стандартного метода JPA для этого (и, по-видимому, это невозможно с Hibernate).

Заставляет меня вернуться к JDO.