好的,这可能是一个微不足道的问题,但我在查看和理解差异以及何时使用每个问题时遇到了麻烦。关于单向和双向映射等概念如何影响一对多/多对多关系,我也有点不清楚。我现在正在使用Hibernate,所以任何与ORM相关的解释都会有所帮助。
作为一个例子,假设我有以下设置:
public class Person{
private Long personId;
private Set<Skill> skills;
//Getters and setters
}
public class Skill{
private Long skillId;
private String skillName;
//Getters and setters
}
那么在这种情况下我会有什么样的映射?这个具体示例的答案肯定是值得赞赏的,但我也非常希望概述何时使用一对多和多对多以及何时使用连接表与连接列以及单向与双向。
答案 0 :(得分:190)
看起来每个人都在回答One-to-many
vs Many-to-many
:
One-to-many
,Many-to-one
和Many-to-Many
之间的差异是:
One-to-many
vs Many-to-one
是一个透视问题。 Unidirectional
vs Bidirectional
不会影响映射,但会对您访问数据的方式产生影响。
One-to-many
many
方将继续引用one
方。一个很好的例子是“一个人有很多技能”。在这种情况下,Person
是一方,Skill
是多方。表person_id
中将有一列skills
。在单向
Person
类中将有List<Skill> skills
但是Skill
将不会Person person
。在双向中 添加属性,它允许您访问给定的Person
技能(即skill.person
)。
Many-to-one
中,许多方面将成为我们的参考点。例如,“用户有地址”。在我们的系统中,许多用户可能共享一个地址(例如,许多人可能共享相同的块号)。在这种情况下,address_id
表中的users
列将由多个users
行共享。在这种情况下,我们说users
和addresses
有Many-to-one
关系。 在单向中,
User
将Address address
。 双向List<User> users
课程中会有额外的Address
。
Many-to-Many
成员中,每一方都可以引用另一方的任意数量的成员。为实现此目的,使用了look up table。这方面的例子是医生和患者之间的关系。医生可以有很多病人,反之亦然。答案 1 :(得分:140)
一对多:一个人有很多技能,一个技能不会在人员之间重复使用
多对多:一个人有很多技能,一个技能在人员之间重复使用
在一对多关系中,一个对象是“父”,一个是“孩子”。父母控制孩子的存在。在“多对多”中,任何一种类型的存在都依赖于它们之外的某些东西(在较大的应用程序上下文中)。
你的主题(领域)应该决定这种关系是一对多还是多对多 - 然而,我发现使关系单向或双向是一种交换记忆的工程决策,处理,表现等。
可能令人困惑的是,多对多双向关系不需要是对称的!也就是说,一群人可以指出一项技能,但技能不需要仅涉及那些人。通常它会,但这种对称性不是必需的。以爱为例 - 它是双向的(“我爱”,“爱我”),但往往是不对称的(“我爱她,但她不爱我”)!
所有这些都得到Hibernate和JPA的很好支持。请记住,在管理双向多对多关系时,Hibernate或任何其他ORM都没有提供关于保持对称性的信息......这完全取决于应用程序。
答案 2 :(得分:33)
1)圈子是实体/ POJO /豆子
2)deg是度数的缩写,如图(边数)
PK =主键,FK =外键
注意程度和名称之间的矛盾。许多对应于度= 1,而一个对应于度> 1。
答案 3 :(得分:11)
这是一个非常常见的问题,因此此答案基于我在博客上写的this article。
一对多表关系如下:
在关系数据库系统中,一对多表关系基于子表中的Foreign Key
列引用父表中一条记录的Primary Key
来关联两个表。
在上面的表图中,post_id
表中的post_comment
列与Foreign Key
表ID post
列具有Primary Key
关系:>
ALTER TABLE
post_comment
ADD CONSTRAINT
fk_post_comment_post_id
FOREIGN KEY (post_id) REFERENCES post
在JPA中,映射一对多表关系的最佳方法是使用@ManyToOne
批注。
在我们的案例中,PostComment
子实体使用post_id
批注映射@ManyToOne
外键列:
@Entity(name = "PostComment")
@Table(name = "post_comment")
public class PostComment {
@Id
@GeneratedValue
private Long id;
private String review;
@ManyToOne(fetch = FetchType.LAZY)
private Post post;
}
@OneToMany
批注仅仅是因为您可以选择使用@OneToMany
批注,但这并不意味着它应该是所有一对多数据库关系的默认选项。
JPA集合的问题在于,我们只能在它们的元素数量很少时使用它们。
映射@OneToMany
关联的最好方法是依靠@ManyToOne
端传播所有实体状态更改:
@Entity(name = "Post")
@Table(name = "post")
public class Post {
@Id
@GeneratedValue
private Long id;
private String title;
@OneToMany(
mappedBy = "post",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<PostComment> comments = new ArrayList<>();
//Constructors, getters and setters removed for brevity
public void addComment(PostComment comment) {
comments.add(comment);
comment.setPost(this);
}
public void removeComment(PostComment comment) {
comments.remove(comment);
comment.setPost(null);
}
}
父Post
实体具有两个实用程序方法(例如addComment
和removeComment
),用于同步双向关联的两端。
无论何时使用双向关联,都应提供这些方法,否则将冒very subtle state propagation issues的风险。
应避免使用单向@OneToMany
关联,因为它比使用@ManyToOne
或双向@OneToMany
关联的效率低。
有关映射
@OneToMany
与JPA和Hibernate关系的最佳方法的更多详细信息,请查看this article。
一对一的表关系如下:
在关系数据库系统中,一对一的表关系基于子级中的Primary Key
列链接两个表,该子表也是Foreign Key
引用子项的Primary Key
父表行。
因此,我们可以说子表与父表共享Primary Key
。
在上面的表图中,id
表中的post_details
列也与Foreign Key
表post
{{1} }列:
id
Primary Key
批注的JPA ALTER TABLE
post_details
ADD CONSTRAINT
fk_post_details_id
FOREIGN KEY (id) REFERENCES post
映射@OneToOne
关系的最好方法是使用@MapsId
。这样,您甚至不需要双向关联,因为您始终可以使用@OneToOne
实体标识符来获取@MapsId
实体。
映射如下:
PostDetails
通过这种方式,Post
属性既可以用作主键又可以用作外键。您会注意到,@Entity(name = "PostDetails")
@Table(name = "post_details")
public class PostDetails {
@Id
private Long id;
@Column(name = "created_on")
private Date createdOn;
@Column(name = "created_by")
private String createdBy;
@OneToOne(fetch = FetchType.LAZY)
@MapsId
@JoinColumn(name = "id")
private Post post;
public PostDetails() {}
public PostDetails(String createdBy) {
createdOn = new Date();
this.createdBy = createdBy;
}
//Getters and setters omitted for brevity
}
列不再使用id
注释,因为标识符是用@Id
关联的标识符填充的。
有关与JPA和Hibernate映射
@GeneratedValue
关系的最佳方法的更多详细信息,请查看this article。
多对多表关系如下所示:
在关系数据库系统中,多对多表关系通过一个子表链接两个父表,该子表包含引用两个父表的post
列的两个@OneToOne
列。
在上面的表图中,Foreign Key
表中的Primary Key
列也与post_id
表ID post_tag
列具有Foreign Key
关系:< / p>
post
并且,Primary Key
表中的 ALTER TABLE
post_tag
ADD CONSTRAINT
fk_post_tag_post_id
FOREIGN KEY (post_id) REFERENCES post
列与tag_id
表ID post_tag
列具有Foreign Key
关系:
tag
Primary Key
映射这是您可以通过JPA和Hibernate映射 ALTER TABLE
post_tag
ADD CONSTRAINT
fk_post_tag_tag_id
FOREIGN KEY (tag_id) REFERENCES tag
表关系的方法:
@ManyToMany
many-to-many
实体中的 @Entity(name = "Post")
@Table(name = "post")
public class Post {
@Id
@GeneratedValue
private Long id;
private String title;
@ManyToMany(cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
@JoinTable(name = "post_tag",
joinColumns = @JoinColumn(name = "post_id"),
inverseJoinColumns = @JoinColumn(name = "tag_id")
)
private Set<Tag> tags = new HashSet<>();
//Getters and setters ommitted for brevity
public void addTag(Tag tag) {
tags.add(tag);
tag.getPosts().add(this);
}
public void removeTag(Tag tag) {
tags.remove(tag);
tag.getPosts().remove(this);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Post)) return false;
return id != null && id.equals(((Post) o).getId());
}
@Override
public int hashCode() {
return getClass().hashCode();
}
}
@Entity(name = "Tag")
@Table(name = "tag")
public class Tag {
@Id
@GeneratedValue
private Long id;
@NaturalId
private String name;
@ManyToMany(mappedBy = "tags")
private Set<Post> posts = new HashSet<>();
//Getters and setters ommitted for brevity
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Tag tag = (Tag) o;
return Objects.equals(name, tag.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
}
关联仅定义tags
和Post
级联类型。正如this article中所述,PERSIST
entity state transition对于MERGE
JPA关联没有任何意义,因为它可能会触发一条链删除操作,从而最终抹去连接的两面。关联。REMOVE
实体将实体标识符用于相等性,因为它缺少任何唯一的业务密钥。如this article中所述,只要确保实体标识符在所有entity state transitions上都保持一致,就可以使用实体标识符。@ManyToMany
实体具有唯一的业务密钥,该业务密钥带有特定于Hibernate的Post
批注。在这种情况下,唯一的业务密钥为the best candidate for equality checks。Tag
实体中@NaturalId
关联的mappedBy
属性表示,在这种双向关系中,posts
实体拥有该关联。这是必需的,因为只有一方可以拥有关系,并且更改仅从该特定方传播到数据库。Tag
,因为将Post
与Set
一起使用效率较低。有关与JPA和Hibernate映射
List
关系的最佳方法的更多详细信息,请查看this article。
答案 4 :(得分:6)
看一下这篇文章:Mapping Object Relationships
映射时需要关注两类对象关系。第一类基于多重性,它包括三种类型:
*One-to-one relationships. This is a relationship where the maximums of each of its multiplicities is one, an example of which is holds relationship between Employee and Position in Figure 11. An employee holds one and only one position and a position may be held by one employee (some positions go unfilled).
*One-to-many relationships. Also known as a many-to-one relationship, this occurs when the maximum of one multiplicity is one and the other is greater than one. An example is the works in relationship between Employee and Division. An employee works in one division and any given division has one or more employees working in it.
*Many-to-many relationships. This is a relationship where the maximum of both multiplicities is greater than one, an example of which is the assigned relationship between Employee and Task. An employee is assigned one or more tasks and each task is assigned to zero or more employees.
第二类是基于 方向性,它包含两个 类型,单向关系 和双向关系。
*Uni-directional relationships. A uni-directional relationship when an object knows about the object(s) it is related to but the other object(s) do not know of the original object. An example of which is the holds relationship between Employee and Position in Figure 11, indicated by the line with an open arrowhead on it. Employee objects know about the position that they hold, but Position objects do not know which employee holds it (there was no requirement to do so). As you will soon see, uni-directional relationships are easier to implement than bi-directional relationships.
*Bi-directional relationships. A bi-directional relationship exists when the objects on both end of the relationship know of each other, an example of which is the works in relationship between Employee and Division. Employee objects know what division they work in and Division objects know what employees work in them.
答案 5 :(得分:1)
这可能需要一个多对多关系,如下所示
public class Person{
private Long personId;
@manytomany
private Set skills;
//Getters and setters
}
public class Skill{
private Long skillId;
private String skillName;
@manyToMany(MappedBy="skills,targetClass="Person")
private Set persons; // (people would not be a good convenion)
//Getters and setters
}
您可能需要定义一个joinTable + JoinColumn,但也可以在没有...
的情况下工作答案 6 :(得分:0)
首先,阅读所有细则。请注意,NHibernate(因此,我假设,Hibernate也是如此)关系映射与DB和对象图映射有一个有趣的对应关系。例如,一对一关系通常被实现为多对一关系。
其次,在我们告诉您如何编写O / R地图之前,我们还必须看到您的数据库。特别是,单个技能可以由多人拥有吗?如果是这样,你就有了多对多的关系;否则,它是多对一的。
第三,我不希望直接实现多对多关系,而是在您的域模型中对“连接表”进行建模 - 即将其视为一个实体,如下所示:
class PersonSkill
{
Person person;
Skill skill;
}
然后你看到你有什么?你有两个一对多的关系。 (在这种情况下,Person可能有一组PersonSkills,但是没有Skills的集合。)但是,有些人更喜欢使用多对多关系(人与技能之间);这是有争议的。
第四,如果你确实有双向关系(例如,不仅Person有一个技能集合,而且Skill有一个人员集合),NHibernate 不在你的BL中强制执行双向性为了你;它只是为了持久性目的而理解关系的双向性。
第五,多对一在NHibernate中更容易正确使用(我假设是Hibernate)而不是一对多(集合映射)。
祝你好运!答案 7 :(得分:0)
我会这样解释:
OneToOne-OneToOne 关系
@OneToOne
Person person;
@OneToOne
Nose nose;
一对多-ManyToOne 关系
@OneToMany
List<Shepherd> shepherd;
@ManyToOne
Sheep sheep;
多对多-多对多关系
@ManyToMany
List<Traveler> traveler;
@ManyToMany
List<Destination> destination;