JPA @OneToOne с общим идентификатором-могу ли я сделать это лучше?
Я работаю с существующей схемой, которую я бы предпочел не менять. Схема имеет взаимно однозначное отношение между таблицами Person и VitalStats, где Person имеет первичный ключ, а VitalStats использует то же поле, что и его первичный ключ и его внешний ключ для Person, то есть его значение является значением соответствующего ПК человека.
эти записи создаются внешними процессами, и мой код JPA никогда не нуждается в обновлении VitalStats. Для моей объектной модели мне нужен мой человек класс, содержащий член 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, который не работает для @сущности.
Если я попробую
@Id @OneToOne
public Person getPerson() { return person; }
это решает проблему @Id, но требует, чтобы человек был Сериализуемым. Мы еще вернемся к этому.
я мог бы сделать VitalStats @Embeddable и подключить его к человеку через @ ElementCollection, но тогда он должен быть доступен как коллекция, даже хотя я знаю, что есть только один элемент. Выполнимо, но и немного раздражает и немного сбивает с толку.
Так что мешает мне просто сказать, что человек реализует Serializable? На самом деле ничего, кроме того, что мне нравится, чтобы все в моем коде было по какой-то причине, и я не вижу никакой логики в этом, что делает мой код менее читаемым.
тем временем я просто заменил поле Person в VitalStats на длинный personId и сделал этот идентификатор @ID VitalStats, поэтому теперь @OneToOne работает.
все эти решения для того, что кажется (мне) простой проблемой, немного неуклюжи, поэтому мне интересно, не пропущу ли я что-нибудь, или кто-то может хотя бы объяснить мне, почему человек должен быть Сериализуемым.
ТИА
2 ответа:
чтобы сопоставить связь один к одному с помощью общих первичных ключей используйте
@PrimaryKeyJoinColumn
и@MapsId
Примечание.соответствующие разделы справочной документации Hibernate:
в аннотации PrimaryKeyJoinColumn говорится, что первичный ключ объект используется в качестве значения внешнего ключа в связанной сущности.
в MapsId аннотации задать спящий режим, чтобы скопировать идентификатор из другая связанная сущность. В гибернации жаргоне, это называется иностранный генератор, но отображение JPA читается лучше и поощряется
человек.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`) )
в моем случае это сделал трюк:
родительский класс:
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; }