@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 их.:)

Другие вопросы по тегам