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