Java Hibernate:OneToOne:相同类型的两个字段
假设我有一个Java Hibernate:OneToOne:相同类型的两个字段,java,hibernate,jpa,inheritance,orm,Java,Hibernate,Jpa,Inheritance,Orm,假设我有一个人实体和一个动物实体;一个人可以有两个最喜欢的动物,一个动物只能有一个喜欢它们的人(一个人喜欢一个动物,其他人就不可能再喜欢/看到那个动物)。这是一个@OneToOne映射:用于Person中的两个Animal字段和Animal中的两个Person字段。但是,在Animal中,第一人称应=到第二人称。在Animal类中只有一个Person字段,是否有办法执行以下操作 Person.java: @Entity @SequenceGenerator(name="PERSON_S
人
实体和一个动物
实体;一个人可以有两个最喜欢的动物,一个动物只能有一个喜欢它们的人(一个人喜欢一个动物,其他人就不可能再喜欢/看到那个动物)。这是一个@OneToOne
映射:用于Person
中的两个Animal
字段和Animal
中的两个Person
字段。但是,在Animal
中,第一人称应=
到第二人称。在Animal
类中只有一个Person
字段,是否有办法执行以下操作
Person.java:
@Entity
@SequenceGenerator(name="PERSON_SEQ", sequenceName="person_sequence")
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator="PERSON_SEQ")
private Long id;
@Column
private String name;
public Person() {}
@OneToOne
@JoinColumn(name = "firstAnimal")
private Animal firstAnimal;
@OneToOne
@JoinColumn(name = "secondAnimal")
private Animal secondAnimal;
//getters and setters
@Entity
@SequenceGenerator(name="PRIVATE_SEQ", sequenceName="private_sequence")
public class Animal {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator="PRIVATE_SEQ")
private Long id;
@Column
private String name;
@OneToOne(mappedBy = "firstAnimal")
private Person firstPerson;
@OneToOne(mappedBy = "secondAnimal")
private Person secondPerson;
...
Animal.java:
@Entity
@SequenceGenerator(name="PERSON_SEQ", sequenceName="person_sequence")
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator="PERSON_SEQ")
private Long id;
@Column
private String name;
public Person() {}
@OneToOne
@JoinColumn(name = "firstAnimal")
private Animal firstAnimal;
@OneToOne
@JoinColumn(name = "secondAnimal")
private Animal secondAnimal;
//getters and setters
@Entity
@SequenceGenerator(name="PRIVATE_SEQ", sequenceName="private_sequence")
public class Animal {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator="PRIVATE_SEQ")
private Long id;
@Column
private String name;
@OneToOne(mappedBy = "firstAnimal")
private Person firstPerson;
@OneToOne(mappedBy = "secondAnimal")
private Person secondPerson;
...
在这种情况下,您可能需要使用@OneToMany/@ManyToOne来获得永久解决方案
Person.java:
@Entity
@Table(name = "...")
public class Person {
@Id
@GeneratedValue(generator = "uuid")
@GenericGenerator(name = "uuid", strategy = "org.hibernate.id.UUIDGenerator")
@Column(name="person_id")
private Long id;
@Column(name="...")
private String name;
@OneToMany(mappedBy = "owner", cascade = CascadeType.?)
private List<Animal> animals;
}
@Entity
@Table(name = "...")
public class Animal {
@Id
@GeneratedValue(generator = "uuid")
@GenericGenerator(name = "uuid", strategy = "org.hibernate.id.UUIDGenerator")
@Column(name = "...")
private Long id;
@Column(name = "...")
private String name;
@ManyToOne
@JoinColumn(name="columnName", referencedColumnName="person_id")
private Person owner;
}
对于进一步的限制,例如一个人最多只能拥有2只喜爱的动物,或者一只动物只能由一个人拥有,您可以使用代码逻辑对其进行检查。就像大家所说的那样-如果一个人对一只动物,但多个动物对一个人,则不能是一对一的关系。这是一种二对一的关系
如果动物的第一人称是人a,第二人称是人a,那么人a中的两个连接性也被使用了
或者,如果设置了第一个人,您可以强制第二个人为null,但只需将其设置为多对1关系即可;) 我认为用OneToOne
实现这种双向映射不是一个简单的方法(正如其他人所指出的,这并不是真正的一对一关联)。即使有,我也会说,考虑到领域模型的要求,您的模型也不可读或不容易理解:当您说“一只动物只能有一个喜欢它们的人”时,我们不应该期望在Animal
类中有两个Person
字段,我们应该期望只有一个
您可以删除双向映射,并在人
中为每个动物
选择简单的单向关联:
@Entity
@SequenceGenerator(name="PERSON_SEQ", sequenceName="person_sequence")
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator="PERSON_SEQ")
private Long id;
@Column
private String name;
public Person() {}
@OneToOne
@JoinColumn(name = "firstAnimal")
private Animal firstAnimal;
@OneToOne
@JoinColumn(name = "secondAnimal")
private Animal secondAnimal;
...
}
@Entity
@SequenceGenerator(name="PRIVATE_SEQ", sequenceName="private_sequence")
public class Animal {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator="PRIVATE_SEQ")
private Long id;
@Column
private String name;
@OneToOne
@JoinColumn(name = "person")
private Person person;
...
}
简而言之,JPA和Hibernate并不是为满足您的要求而设计的。无法通过注释和映射对此进行配置
较长的答案是,您可以通过结合使用瞬态字段和数据库配置来获得所需的行为
如果映射了两个不同的一对一字段,可以在动物内部添加一个临时字段,首先检查一个字段,然后检查另一个字段。如果第一个字段不为空,则返回它。否则,返回第二个字段(不需要第二次空检查)
如果您想要强制一个动物只被一个人喜欢,那么也没有办法通过标准JPA注释或特定于hibernate的注释来表示这一点。可以添加一些检查约束、触发器和索引,以保证每只动物“like”的唯一性,但任何注释都无法做到这一点。数据库或OOP范例中的设计都是不可伸缩的。在现实领域中,您可以在db中拥有尽可能多的列或实体中的字段,就像一个人拥有的动物一样,而不考虑其类型(收藏夹)
在数据库中,您甚至不遵守。同样在基数方面,你总是有0,1或n。没有2这样的东西,2是n
你可以重新设计你的模型,考虑用注释@OneToMany
在科特林,这将是:
@OneToMany(cascade = [CascadeType.ALL])
@JoinColumn(name = "animal_id", referencedColumnName = "animal_id")
val animals: List<Animal>
GL我知道如何使用OneToMany
来做这件事。问题的关键是如何使用OneToOne
。为什么你想让这两个OneToOne
都是双向的,如果正如你在问题中所说的,这两个引用都应该指向同一个Person
?@SternK那么还有什么替代方法呢?“一个人可以有两个最喜欢的动物[…]这是@OneToOne映射” → 这显然是一个矛盾。但是,如果你看不出原因,请考虑一下:映射反映的是实体之间的关系,而不是特定字段之间的关系。如果一个Person
与多个(“多”)动物相关,即如果SELECT*FROM Animal,其中FIRSTPERSON=xyz
可能(并且通常会)返回“多”行,则Person
和动物
之间的关系为一对多。(我不会解释如何反映@OneToMany关系,因为您已经注释了您知道该怎么做。)但在我的示例中,SELECT*FROM ANIMAL,其中FIRSTPERSON=xyz
将返回1行。比如说,animals
表将有两列-firstperson
和secondperson
。如果id为4
的特定动物有一个人57
作为第一人
,则其他动物不能将该人作为第一人
。同时,57
人将在firstanimal
列中显示动物4
,@瓦伦和其他任何人都不会将该动物作为firstanimal
:select*from persons其中firstanimal=4
将返回一行id=57,而select*from animals其中firstanimal=57
将返回一行id=4