简体   繁体   English

Hibernate:OneToOne:相同类型的两个字段

[英]Hibernate: OneToOne: two fields of the same type

Suppose I have a Person entity and an Animal entity;假设我有一个Person实体和一个Animal实体; A Person can have two favourite animals and an Animal can only have one Person that likes them (one person liking an animal makes it no longer possible for other people to like/see that animal).一个Person可以有两个最喜欢的动物和Animal只能有一个Person ,喜欢他们(一个人喜欢动物使它不再可能别人喜欢/看到动物)。 It is a @OneToOne mapping: for the two Animal fields in Person and for the two Person fields in Animal .这是一个@OneToOne映射:两个Animal的领域Person和两个Person在字段中Animal However, in Animal firstPerson should == to secondPerson .但是,在Animal firstPerson应该==secondPerson Is there a way to do the following with only one Person field in the Animal class?有没有办法只用Animal类中的一个Person字段来执行以下操作?

Person.java :人.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

Animal.java :动物.java :

@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;
...

You might want to use @OneToMany / @ManyToOne in this scenario for a perpetual solution.您可能希望在这种情况下使用@OneToMany / @ManyToOne来获得永久解决方案。

Person.java:人.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;
    
}

Animal.java:动物.java:

@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;

}

For further constrains such as one person can only have maximum 2 favourite animals or one animal can only be owned by 1 person, you can have them checked using code logic.对于进一步的限制,例如一个人最多只能拥有 2 只最喜欢的动物或一只动物只能由 1 人拥有,您可以使用代码逻辑检查它们。

I don't think there is an easy way to implement this bidirectional mapping with a OneToOne (as the others pointed out, this is not really a one-to-one association).我认为没有一种简单的方法可以使用OneToOne实现这种双向映射(正如其他人指出的那样,这并不是真正的一对一关联)。 Even if there was, I would argue your model would not be readable or easy to understand given the domain model requirements: when you say "an Animal can only have one Person that likes them" , we shouldn't expect to have two Person fields in the Animal class, we should expect simply one.即使有,我也会争辩说,鉴于域模型要求,您的模型不可读或不易于理解:当您说“一个动物只能有一个喜欢它们的人”时,我们不应该期望有两个Person字段在Animal类中,我们应该只期待一个。

You can drop the bidirectional mapping and opt for a simple unidirectional association for each Animal in the Person :您可以删除双向映射并为Person中的每个Animal选择简单的单向关联:

@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;

    ...
}

The short answer is that JPA and Hibernate aren't designed to do what you are asking.简短的回答是 JPA 和 Hibernate 不是为了满足您的要求而设计的。 There is no way to configure this via annotations and mappings.无法通过注释和映射来配置它。

The longer answer is that you can kind of get the behavior that you are looking for using a combination of transient fields and database configuration...更长的答案是,您可以使用瞬态字段和数据库配置的组合来获得您正在寻找的行为......

If you mapped 2 different one-to-one fields, you could add a transient field inside of the Animal that checks first one field and then the other.如果映射了 2 个不同的一对一字段,则可以在 Animal 中添加一个临时字段,先检查一个字段,然后再检查另一个。 If the first field is not-null, return it.如果第一个字段不为空,则返回它。 Otherwise, return the second field (no need for a second null-check).否则,返回第二个字段(不需要第二个空检查)。

If you want to enforce that an Animal is only liked by one person, there is also no way to represent this via standard JPA annotations or via hibernate-specific annotations.如果你想强制一个动物只被一个人喜欢,也没有办法通过标准的 JPA 注释或休眠特定的注释来表示这一点。 It would be possible to add some check-constraints, triggers, and indexes that would guarantee the uniqueness of "like" per Animal, but you don't get that out-of-the-box with any of the annotations.可以添加一些检查约束、触发器和索引,以保证每个 Animal 的“like”的唯一性,但是您无法通过任何注释获得开箱即用的功能。

Like everyone has said - If you have one person to an animal, but multiple animals to a person it cannot be a one-to-one relationship.就像每个人都说的 - 如果你有一个人对一只动物,但对一个人有多个动物,那不可能是一对一的关系。 That is a two-to-one relationship.那是一对一的关系。

If the animals first person is person a, and their second person is person a, then both connectios in person a have been used too.如果动物的第一人称是人 a,而他们的第二人称是人 a,那么也使用了人 a 中的两个连接。

Alternatively, you could force that second person is null if first person is set - but then just make it a many to 1 relationship like it should be ;)或者,如果设置了第一人称,您可以强制第二人为空 - 但随后只需将其设为多对一的关系即可;)

That design in both the database or OOP paradigm is not scalable at all.数据库或 OOP 范式中的这种设计根本无法扩展。 In the domain of reality you could have as many columns in the db or fields in your entity as there are animals a person has regardless of the type (favorite).在现实领域中,您可以在实体中的数据库或字段中拥有与一个人拥有的动物一样多的列,而不管其类型(最喜欢的)是什么。

In databases you are not complying with even the first normal formal .在数据库中,您甚至不遵守第一个正常的正式. Also in terms of cardinalities you always have 0, 1 or n.同样在基数方面,您总是有 0、1 或 n。 There is no such thing as 2, 2 is n.没有 2 这样的东西,2 就是 n。

You could redesign your model thinking about relating the person with a list of animals with the annotation @OneToMany你可以重新设计你的模型,考虑用@OneToMany注释将人与动物列表联系@OneToMany

In Kotlin it would be:在 Kotlin 中,它将是:

@OneToMany(cascade = [CascadeType.ALL])
@JoinColumn(name = "animal_id", referencedColumnName = "animal_id")
val animals: List<Animal>

On the other hand, you can add an enum to the animal that represents the type.另一方面,您可以向代表该类型的动物添加一个枚举

enum class TypeAnimal {
        FAVOURITE
}

GL GL

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM