Какие гарантии для данных дает внешний ключ в реляционной базе данных?
Что проверяют
Понимаете ли вы, что именно обещает база, а что остаётся на совести кода. Следом обычно спрашивают про ON DELETE, про индекс на внешнем ключе и чем каскад в базе отличается от каскада в JPA.
Как отвечать
Внешний ключ даёт ссылочную целостность: в дочерней таблице не может оказаться значения, которого нет в родительской. Ссылаться можно на первичный ключ или на колонки с ограничением UNIQUE. Гарантия действует для всех, кто пишет в базу, включая миграции и ручные запросы.
Что конкретно проверяется:
- вставка и обновление в дочерней таблице: ссылка на несуществующую строку отклоняется, в Spring это превращается в
DataIntegrityViolationException; - удаление и смена ключа в родительской: поведение задают
ON DELETEиON UPDATEс вариантамиNO ACTION(по умолчанию),RESTRICT,CASCADE,SET NULL,SET DEFAULT; NULL: внешний ключ не запрещает пустое значение, для обязательной связи нужен ещёNOT NULL. В составном ключе в режиме по умолчанию хватает одной пустой колонки, чтобы проверка не выполнялась;- отложенная проверка: в PostgreSQL ограничение можно объявить
DEFERRABLEи проверять в конце транзакции.NO ACTIONоткладывается,RESTRICTсрабатывает сразу.
Чего внешний ключ не даёт. В PostgreSQL он не создаёт индекс на ссылающейся колонке (MySQL с InnoDB создаёт сам), и без индекса удаление родителя сканирует всю дочернюю таблицу. Бизнес-правила вроде «у заказа есть хотя бы одна позиция» он тоже не проверяет.
Java-сторона: @ManyToOne с @JoinColumn описывает связь, но cascade = CascadeType.REMOVE выполняет Hibernate, загружая и удаляя сущности по одной, а ON DELETE CASCADE выполняет сама база. Чтобы Hibernate опирался на каскад базы, есть @OnDelete(action = OnDeleteAction.CASCADE).
Где спотыкаются
Считают CascadeType.REMOVE и ON DELETE CASCADE одним и тем же и удивляются тысячам отдельных DELETE в логах.
Не ставят индекс на колонку внешнего ключа, и удаление из родительской таблицы превращается в долгий полный скан с блокировками.
Отказываются от внешних ключей «ради скорости» и потом годами чистят строки-сироты, которые оставили баги и ручные правки.