JPA @OneToOne с общим ID - Могу ли я сделать это лучше?
Im работает с существующей схемой, которую Id скорее не изменяет. Схема имеет взаимно однозначную взаимосвязь между таблицами Person и VitalStats, где Person имеет первичный ключ, а VitalStats использует то же поле, что и его первичный ключ, и его внешний ключ для Person, что означает его значение - значение соответствующего PK человека.
Эти записи создаются внешними процессами, а мой JPA-код никогда не нуждается в обновлении VitalStats.
Для моей объектной модели Id, такой как мой класс Person, чтобы содержать элемент VitalStats, BUT:
Когда я пытаюсь
@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, но тогда его нужно будет получить как коллекцию, хотя я знаю, что это только один элемент. Возможность, но и немного раздражающая и немного запутанная.
Итак, что мешает мне просто сказать, что Личность реализует Serializable? Ничего, действительно, за исключением того, что мне нравится все в моем коде, чтобы быть там по какой-то причине, и я не вижу в этом никакой логики, что делает мой код менее удобочитаемым.
Тем временем я просто заменил поле Person в VitalStats длинным personId и сделал VitalStatss @Id, так что теперь работает @OneToOne.
Все эти решения, которые кажутся мне похожими на простые проблемы, немного неуклюжи, поэтому я задаюсь вопросом, не пропадает ли я что-нибудь или кто-то может хотя бы объяснить мне, почему Person должен быть Serializable.
ТИА
Ответы
Ответ 1
Для сопоставления взаимно-однозначной ассоциации с использованием общих первичных ключей используйте аннотацию @PrimaryKeyJoinColumn
и @MapsId
.
Соответствующие разделы справочной документации по гибернату:
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;
}
Таблица базы данных Person
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`)
)
Ответ 2
В моем случае это сделало трюк:
Родительский класс:
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;
}