JPA @ OneToOne с общим идентификатором - Могу ли я сделать это лучше? - PullRequest
60 голосов
/ 26 июля 2011

Я работаю с существующей схемой, которую лучше не менять. Схема имеет взаимно-однозначное отношение между таблицами Person и VitalStats, где Person имеет первичный ключ, а VitalStats использует то же поле, что и первичный ключ, и внешний ключ для Person, что означает, что его значение является значением соответствующего PK человека.

Эти записи создаются внешними процессами, и мой код JPA никогда не нуждается в обновлении VitalStats. Для моей объектной модели я хотел бы, чтобы мой класс Person содержал член VitalStats, НО:

Когда я пытаюсь

@Entity
public class Person{
    private long id;
    @Id
    public long getId(){ return id; }

    private VitalStats vs;
    @OneToOne(mappedBy = “person”)
    public VitalStats getVs() { return vs; }
}

@Entity
    public class VitalStats{
     private Person person;
    @OneToOne
    public Person getPerson() { return person; }
}

У меня проблема с тем, что VitalStats не хватает @Id, который не работает для @Entity. \

Если я попытаюсь

@Id @OneToOne
public Person getPerson() { return person; }

, который решает проблему @Id, но требует, чтобы Person был Serializable. Мы вернемся к этому.

Я мог бы сделать VitalStats @Embeddable и подключить его к Person с помощью @ElementCollection, но тогда к нему нужно будет обращаться как к коллекции, даже если я знаю, что есть только один элемент. Выполнимо, но и немного раздражает, и немного сбивает с толку.

Так что же мешает мне просто сказать, что Person реализует Serializable? На самом деле ничего, кроме того, что мне нравится, что все в моем коде присутствует по какой-то причине, и я не вижу в этом никакой логики, что делает мой код менее читабельным.

Тем временем я просто заменил поле Person в VitalStats длинным personId и сделал его @Id, так что теперь @OneToOne работает.

Все эти решения того, что кажется (на мой взгляд) простой проблемой, немного неуклюжие, поэтому мне интересно, что я что-то упускаю или кто-то может хотя бы объяснить мне, почему Person должен быть сериализуемым .

ТИА

Ответы [ 2 ]

86 голосов
/ 27 июля 2011

Чтобы отобразить сопоставление «один к одному» с использованием общих первичных ключей, используйте аннотации @PrimaryKeyJoinColumn и @MapsId.

Соответствующие разделы справочной документации по Hibernate:

PrimaryKeyJoinColumn

Аннотация PrimaryKeyJoinColumn говорит, что первичный ключ объект используется в качестве значения внешнего ключа для ассоциированного объекта.

MapsId

Аннотация MapsId просит Hibernate скопировать идентификатор из другой связанный объект. На спящем жаргонном языке он известен как посторонний генератор, но отображение JPA читается лучше и приветствуется

Person.java

@Entity
public class Person {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "person_id")
    private Long id;

    @OneToOne(cascade = CascadeType.ALL)
    @PrimaryKeyJoinColumn
    private VitalStats vitalStats;       
}

VitalStats.java

@Entity
public class VitalStats 
{
    @Id @Column(name="vitalstats_id") Long id;

    @MapsId 
    @OneToOne(mappedBy = "vitalStats")
    @JoinColumn(name = "vitalstats_id")   //same name as id @Column
    private Person person;

    private String stats;
}

Таблица базы данных персонала

CREATE TABLE  person (
  person_id   bigint(20) NOT NULL auto_increment,
  name        varchar(255) default NULL,
  PRIMARY KEY  (`person_id`)
) 

Таблица базы данных VitalStats

CREATE TABLE  vitalstats 
(
  vitalstats_id  bigint(20) NOT NULL,
  stats          varchar(255) default NULL,
  PRIMARY KEY  (`vitalstats_id`)
)
16 голосов
/ 10 апреля 2016

В моем случае это сделал трюк:

Родительский класс:

public class User implements Serializable {
  private static final long serialVersionUID = 1L;

  /** auto generated id (primary key) */
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(unique = true, nullable = false)
  private Long id;

  /** user settings */
  @OneToOne(cascade = CascadeType.ALL, mappedBy = "user")
  private Setting setting;
}

Дочерний класс:

public class Setting implements Serializable {
  private static final long serialVersionUID = 1L;

  /** setting id = user id */
  @Id
  @Column(unique = true, nullable = false)
  private Long id;

  /** user with this associated settings */
  @MapsId
  @OneToOne
  @JoinColumn(name = "id")
  private User user;
}
...