英文:
Hibernate: OneToOne: two fields of the same type
问题
@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 person;
...
英文:
Suppose I have a Person
entity and an Animal
entity; 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). It is a @OneToOne
mapping: for the two Animal
fields in Person
and for the two Person
fields in Animal
. However, in Animal
firstPerson
should ==
to secondPerson
. Is there a way to do the following with only one Person
field in the Animal
class?
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
Animal.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;
...
答案1
得分: 4
你可能想在这种情况下使用 @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;
}
Animal.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;
}
对于进一步的限制,例如一个人只能拥有最多2只最喜欢的动物,或者一个动物只能归属于一个人,你可以通过代码逻辑来进行检查。
英文:
You might want to use @OneToMany / @ManyToOne in this scenario for a perpetual solution.
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;
}
Animal.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
得分: 2
我认为使用OneToOne
来实现这种双向映射并不容易(正如其他人指出的,这实际上不是一个一对一的关联)。即使可以,我会认为根据领域模型的要求,你的模型在可读性和易理解性方面也不会很好:当你说“一个动物只能有一个喜欢它的人”的时候,我们不应该在Animal
类中有两个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;
...
}
英文:
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). 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.
You can drop the bidirectional mapping and opt for a simple unidirectional association for each Animal
in the 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;
...
}
答案3
得分: 2
简短的回答是,JPA和Hibernate并不设计用于执行你所要求的操作。无法通过注解和映射来进行此配置。
稍长的回答是,你可以通过使用瞬态字段和数据库配置的组合来实现类似的行为……
如果你映射了两个不同的一对一字段,你可以在Animal类中添加一个瞬态字段,首先检查一个字段,然后再检查另一个字段。如果第一个字段非空,就返回它。否则,返回第二个字段(无需进行第二次空检查)。
如果你想要确保Animal只被一个人喜欢,同样也无法通过标准的JPA注解或Hibernate特定的注解来表示。可以通过添加一些检查约束、触发器和索引来保证每个Animal的“喜欢”是唯一的,但这并不是使用任何注解的开箱即用功能。
英文:
The short answer is that JPA and Hibernate aren't designed to do what you are asking. 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. 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. 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.
答案4
得分: 1
像每个人都说的 - 如果一个人对应一个动物,但一个人对应多个动物,这就不可能是一对一的关系。这是一种二对一的关系。
如果动物的第一个人是人A,而他们的第二个人也是人A,那么人A中的两个连接已经都被使用了。
或者,你可以强制如果第一个人已经设置,那么第二个人就为空 - 但那样就变成了应该是多对一的关系 😉
英文:
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.
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
答案5
得分: 0
这种设计无论是在数据库还是面向对象的范式中都不具有可扩展性。在现实领域中,无论动物的类型(喜欢的类型)如何,您都可以在数据库中拥有与一个人拥有的动物数量一样多的列或实体字段。
在数据库中,您甚至没有遵守第一范式。而且在基数方面,您始终拥有0、1或n。没有2这样的情况,2就是n。
您可以重新设计您的模型,考虑使用注解 @OneToMany
将人与动物列表关联起来。
在 Kotlin 中,可以这样做:
@OneToMany(cascade = [CascadeType.ALL])
@JoinColumn(name = "animal_id", referencedColumnName = "animal_id")
val animals: List<Animal>
另一方面,您可以在动物实体中添加一个表示类型的枚举。
enum class TypeAnimal {
FAVOURITE
}
祝好!
英文:
That design in both the database or OOP paradigm is not scalable at all. 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. There is no such thing as 2, 2 is n.
You could redesign your model thinking about relating the person with a list of animals with the annotation @OneToMany
In Kotlin it would be:
@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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论