@PrimaryKeyJoinColumn не выбирает общий ключ
Раньше у меня работал @PrimaryKeyJoinColumn, сейчас я пытаюсь использовать весеннюю загрузку и не могу понять, чего мне не хватает, это очень странно, так как кажется, я все сделал правильно:
Персональный класс:
@Table(name = "PERSON")
@Entity
@Getter
@Setter
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ID")
private Long id;
@Column(name = "NAME")
private String name;
@OneToOne(cascade = CascadeType.PERSIST)
@PrimaryKeyJoinColumn
private Department department;
}
Отдел класса:
@Table(name = "DEPARTMENT")
@Entity
@Getter
@Setter
public class Department {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ID")
private Long id;
@Column(name = "NAME")
private String name;
@OneToOne(mappedBy = "department")
private Person person;
}
Класс обслуживания:
@Transactional
public void addPerson() {
Person person = new Person();
person.setName("person 1");
Department department = new Department();
department.setName("test");
department.setPerson(person);
person.setDepartment(department);
personRepository.save(person);
}
Вот что я получаю в таблице БД: Person:
ID Name
13 person 1
Стол отдела:
ID Name
18 test
Желаемый результат: оба идентификатора должны быть идентичны (идентификатор лица должен быть 18, а не 13).
Обновление: hibernate всегда пытается вставить человека без идентификатора, поэтому, если я удаляю автоинкремент из идентификатора человека и пытается вставить человека с существующим отделом, я получаю:
Hibernate: insert into person (name) values (?)
Field 'id' doesn't have a default value
Обновление 2: кажется, @PrimaryKeyJoinColumn не будет обрабатывать генерацию идентификатора как класс отдела, поэтому мне нужно использовать генератор. Я удивляюсь, потому что те же самые аннотации работ в Inheritance объединяются, ничего не делая с идентификатором подкласса. Поэтому я ожидаю ответа, объясняющего, почему создание идентификаторов работает в объединенном наследовании, в то время как OneToOne нужен генератор
1 ответ
Несмотря на то, что по крайней мере плохое наименование - создавать целый Отдел только для одного человека (отдел обычно объединяет много людей), я предполагаю, что вы действительно ищете отношения OneToOne с общим PRIMARY KEY.
Лучшее решение (оптимальная память, скорость и обслуживание) заключается в использовании @MapsId
:
@Getter
@Setter
@Entity
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToOne(mappedBy = "person", fetch = FetchType.LAZY)
private Department department;
}
@Getter
@Setter
@Entity
public class Department {
@Id // Note no generation
private Long id;
private String name;
@OneToOne(fetch = FetchType.LAZY)
@MapsId
@JoinColumn(name = "id", foreignKey = @ForeignKey(name = "department_belongs_to_person"))
private Person person;
}
В данном случае Department является владельцем отношения (владение обычно означает, что в базе данных есть столбец с FK, но здесь они просто используют первичный ключ) и владеет FK, который связывает свой PK с генерируемым Person.
Обратите внимание, что отношение OneToOne, двунаправленное, с общим PK в качестве FK. Это считается наилучшей практикой, но имеет один небольшой недостаток, заключающийся в необходимости писать один метод получения.:)
Источники: https://vladmihalcea.com/the-best-way-to-map-a-onetoone-relationship-with-jpa-and-hibernate/
Кроме того - я настоятельно рекомендую потратить несколько дней на чтение постов на этом сайте и даже реализовать несколько из них, прежде чем создавать что-то большее, чем несколько таблиц.:)
РЕДАКТИРОВАТЬ
Я могу ошибаться, но, насколько мне известно, сохранение идентификаторов не является чем-то, что определяет JPA. То, что это может указать, в основном:
"Эй, ребята (Person, Department) - братья (OneToOne), и оба знают это (двунаправленный с mappedBy =" person "в сущности Person). Вы будете старше, брат, который будет генерировать идентификаторы (Person имеет @GeneratedValue), и вы будет тем самым, у которого должно быть то же самое. Вы будете использовать эти поля (идентификаторы, одно сгенерированное, второе нет) для подключения (@PrimaryKeyJoinColumn). "
Я пытаюсь сказать, что если вы говорите "это связывает вас", это не значит, что они синхронизированы - вы должны это гарантировать.
Теперь о том, как это обеспечить - известно, что @MapsId - лучший.
Если вы ищете другие подходы, есть также установка идентификатора вручную, чтобы он совпадал с другим с #setDepartment (Department), где вы должны установить идентификатор Отдела таким же, как и для вызывающего абонента (но это работает, только если у указанного лица уже был создан идентификатор, что в принципе нарушает идею).
Другой известный мне использует иностранную генераторную стратегию.
Человек:
@Id
@GeneratedValue
private long id;
@OneToOne(mappedBy="person")
private Department department;
Департамент:
@Id
@GeneratedValue(generator="gen")
@GenericGenerator(name="gen", strategy="foreign", parameters=@Parameter(name="property", value="person"))
private long id;
@OneToOne
@PrimaryKeyJoinColumn
private Person person;
Теперь - это использует специфичные для hibernate аннотации и не является чистым JPA.
Я также не совсем уверен, что я написал это правильно, но посмотрите на документы для тех или Google их.:)