#spring #hibernate #jpa #one-to-many
Вопрос:
Имея эти классы:
Vehicle.java:
@Data
@Entity
@NoArgsConstructor(force = true)
@RequiredArgsConstructor
public class Vehicle {
@Id
private final int vehicleId;
private final String vehicleName;
}
User.java:
@Entity
@Data
@Table(name = "UserDetails")
public class User {
@Id
private int userId;
private String userName;
@OneToMany
@JoinColumn(name = "vehicleId")
private Collection<Vehicle> vehicles = new ArrayList<>();
}
DemoApp.java:
@Bean
CommandLineRunner dataLoader3(VehicleRepository vehicleRepo, UserRepository userRepo){
return new CommandLineRunner() {
@Override
public void run(String... args) throws Exception {
Vehicle v = new Vehicle(0, "Car");
vehicleRepo.save(v);
Vehicle v2 = new Vehicle(1, "Bus");
vehicleRepo.save(v2);
User u = new User();
u.setUserName("First User");
u.setVehicles(Arrays.asList(v, v2));
userRepo.save(u);
}
}
}
Пружина выдает 2 ошибки относительно внешнего ключа:
Ошибка выполнения DDL «изменить таблицу транспортного средства удалить внешний ключ FKk65fmqecth7bpxy163dwj685f» с помощью инструкции JDBC
и затем
Невозможно добавить или обновить дочернюю строку: сбой ограничения внешнего ключа (
test_db
.vehicle
, ОГРАНИЧЕНИЕFKk65fmqecth7bpxy163dwj685f
ВНЕШНЕГО КЛЮЧА (vehicle_id
) ССЫЛКИuser_details
(user_id
))
Я только что попытался изменить @JoinColumn
имя в @OneToMany
отношении. Как это решить?
PS: Я удалил все созданные таблицы раньше, поэтому ограничений не осталось.
PPS: использую MySQL.
Комментарии:
1. «изменить таблицу, удалить внешний ключ FKk65fmqecth7bpxy163dwj685f» — почему это происходит, если вы удалили все таблицы? Что-то вызывает DDL, чтобы попытаться удалить ограничение, и ошибка может дать ключ к проблеме, с которой вы столкнулись, тем более, что в нем говорится, что оно не может удалить его, и это же ограничение мешает какому-либо другому оператору обновления. Поскольку в нем упоминается «VEHICLE_ID», а вы используете «VehicleId», я подозреваю, что вы используете генерацию DDL и позволяете ей пытаться «исправить» существующую схему, но она не может. Полностью отбросьте это, убедитесь, что они исчезли, исправьте свои сущности и начните все сначала.
Ответ №1:
Проблема связана с определением @JoinColumn(имя = «Идентификатор транспортного средства») для сущности пользователя:
@OneToMany
@JoinColumn(name = "vehicleId")
private Collection<Vehicle> vehicles = new ArrayList<>();
Это указывает JPA на создание и использование столбца «Идентификатор транспортного средства» в таблице транспортных средств для ссылки на сущность пользователя. Ваша сущность/таблица транспортного средства, однако, уже определяет «Идентификатор транспортного средства», что вызывает ошибки и конфликты, особенно если один пользователь ссылается на два транспортных средства, которые, вероятно, попытаются использовать одно и то же значение для идентификатора транспортного средства.
Попробуй:
@OneToMany
@JoinColumn(name = "USER_ID")
private Collection<Vehicle> vehicles = new ArrayList<>();
вместо этого в транспортном средстве будет создан столбец «Идентификатор пользователя» для идентификации одного (и только одного) пользователя, который может быть связан с транспортным средством в этом типе сопоставления. Обычно это делается с помощью
public class User {
@Id
private int userId;
private String userName;
@OneToMany(mappedBy="user")
private Collection<Vehicle> vehicles = new ArrayList<>();
}
public class Vehicle {
@Id
private final int vehicleId;
private final String vehicleName;
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="USER_ID")
private User user;
}
В качестве альтернативы, как было предложено, вы можете просто отказаться от совместной колонки. Это создаст таблицу соединений для этой связи, которая будет очень похожа на множество отношений, но с ограничениями на таблицу отношений.
Комментарии:
1. Это работает (есть
@JoinColumn(name="userId")
, но если я это запущу, jpa создаст только одно транспортное средство (v2
«Автобус»), которое вставлено. Этогоv1
(«Автомобиля») нет. Это как-то связано с этим?2. Этого не должно быть — что происходит при втором вызове сохранения транспортного средства? Включите ведение журнала SQL, чтобы узнать, что делает JPA/Spring во время этого вызова.
Ответ №2:
В вашей настройке вы не должны использовать @JoinColumn(name = "vehicleId")
в User
классе. Пожалуйста, попробуйте следующее (я предполагаю, что Vehicle
ссылки, на которые нет ни в одном User
, не нужны):
@Entity
@Data
@Table(name = "UserDetails")
public class User {
@Id
private int userId;
private String userName;
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
private Collection<Vehicle> vehicles = new ArrayList<>();
}
Предполагается ли однонаправленная связь?
Комментарии:
1. Почему нельзя использовать объединенную колонку? Как исходная форма с столбцом соединения, так и эта без него делают это однонаправленным отображением OneToMany — единственная разница в том, что для вашей требуется дополнительная таблица соединений, в то время как операции использовали внешний ключ в целевой таблице (транспортное средство), аналогично двунаправленному 1:m.
2. Мне следовало бы быть более подробным в предложении
you should not use @JoinColumn
, вы правы. Я имелyou should not use @JoinColumn(name = "vehicleId")
в виду причины, которые вы подробно описали. Что касается однонаправленных отношений, я знал, что мое предложение также создает их, мне просто было интересно, действительно ли это было задумано.