Как сохранить адрес пользователя при хранении адреса в отдельной таблице
Что проверяют
Понимаете ли вы связь таблиц на уровне базы и то, как её отражает JPA: где лежит внешний ключ, кто владелец связи и в каком порядке идут вставки. Следом обычно спрашивают про каскады и N+1 при загрузке пользователей с адресами.
Как отвечать
Сначала выясните кардинальность: один адрес на пользователя или несколько (доставка, регистрация). От этого зависит схема.
В базе внешний ключ user_id лежит в таблице addresses и ссылается на users(id). Для связи один к одному на user_id ставится UNIQUE. PostgreSQL сам индекс на внешний ключ не создаёт, его объявляют отдельно, иначе поиск адресов пользователя и удаление пользователя идут полным сканированием.
В JPA для нескольких адресов:
Address: полеuserс@ManyToOne(fetch = FetchType.LAZY)и@JoinColumn(name = "user_id"). Это владеющая сторона, именно она пишетuser_id;User:@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true), гдеmappedByговорит, что колонку ведёт другая сторона;- метод-помощник:
addAddress(Address a)добавляет адрес в список и вызываетa.setUser(this), чтобы обе стороны совпадали; - транзакция: сервис с
@TransactionalвызываетuserRepository.save(user), каскад сохраняет адреса. Hibernate вставляет пользователя, получаетid, затем пишет адреса.
На чистом JDBC та же логика вручную: вставка пользователя, ключ через RETURNING id или getGeneratedKeys(), вставка адреса, всё в одной транзакции с откатом при ошибке.
Если адрес не живёт отдельно и не нужен другим сущностям, назовите альтернативу: @Embeddable хранит его колонками в той же таблице users.
Где спотыкаются
Заполняют только сторону User: кладут адрес в список, но не вызывают setUser. Hibernate пишет user_id по владеющей стороне, и в базу уходит NULL или падает ограничение NOT NULL.
Оставляют @ManyToOne без LAZY. По спецификации JPA он загружается жадно, и выборка списка адресов тянет пользователя для каждого, это классический N+1.
Вставляют пользователя и адрес в разных транзакциях. При ошибке на втором шаге остаётся пользователь без адреса, хотя по бизнес-правилам адрес обязателен.