Java 一对多、多对一和多对多之间的区别?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3113885/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 16:34:40  来源:igfitidea点击:

Difference Between One-to-Many, Many-to-One and Many-to-Many?

javahibernateormmany-to-manyone-to-many

提问by Ian Dallas

Ok so this is probably a trivial question but I'm having trouble visualizing and understanding the differences and when to use each. I'm also a little unclear as to how concepts like uni-directional and bi-directional mappings affect the one-to-many/many-to-many relationships. I'm using Hibernate right now so any explanation that's ORM related will be helpful.

好的,所以这可能是一个微不足道的问题,但我无法想象和理解差异以及何时使用每个差异。我也有点不清楚像单向和双向映射这样的概念如何影响一对多/多对多关系。我现在正在使用 Hibernate,因此任何与 ORM 相关的解释都会有所帮助。

As an example let's say I have the following set-up:

例如,假设我有以下设置:

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
}

So in this case what kind of mapping would I have? Answers to this specific example are definitely appreciated but I would also really like an overview of when to use either one-to-many and many-to-many and when to use a join table versus a join column and unidirectional versus bidirectional.

那么在这种情况下,我会有什么样的映射?对这个特定示例的回答绝对值得赞赏,但我也非常想了解何时使用一对多和多对多以及何时使用连接表与连接列以及单向与双向。

采纳答案by HDave

One-to-Many: One Person Has Many Skills, a Skill is not reused between Person(s)

一对多:一个人有很多技能,一个技能不会在人之间重复使用

  • Unidirectional: A Person can directly reference Skills via its Set
  • Bidirectional: Each "child" Skill has a single pointer back up to the Person (which is not shown in your code)
  • 单向:一个人可以通过它的集合直接引用技能
  • 双向:每个“子”技能都有一个指向 Person 的指针(未在您的代码中显示)

Many-to-Many: One Person Has Many Skills, a Skill is reused between Person(s)

多对多:一个人有很多技能,一个技能在人之间重复使用

  • Unidirectional: A Person can directly reference Skills via its Set
  • Bidirectional: A Skill has a Set of Person(s) which relate to it.
  • 单向:一个人可以通过它的集合直接引用技能
  • 双向:一项技能有一组与之相关的人员。

In a One-To-Many relationship, one object is the "parent" and one is the "child". The parent controls the existence of the child. In a Many-To-Many, the existence of either type is dependent on something outside the both of them (in the larger application context).

在一对多关系中,一个对象是“父”,一个是“子”。父母控制孩子的存在。在多对多中,任何一种类型的存在都依赖于它们之外的东西(在更大的应用程序上下文中)。

Your subject matter (domain) should dictate whether or not the relationship is One-To-Many or Many-To-Many -- however, I find that making the relationship unidirectional or bidirectional is an engineering decision that trades off memory, processing, performance, etc.

您的主题(领域)应该决定关系是一对多还是多对多——但是,我发现使关系单向或双向是一个工程决策,需要权衡内存、处理、性能, 等等。

What can be confusing is that a Many-To-Many Bidirectional relationship does not need to be symmetric! That is, a bunch of People could point to a skill, but the skill need not relate back to just those people. Typically it would, but such symmetry is not a requirement. Take love, for example -- it is bi-directional ("I-Love", "Loves-Me"), but often asymmetric ("I love her, but she doesn't love me")!

令人困惑的是,多对多双向关系不需要对称!也就是说,一群人可以指向一项技能,但该技能不一定只与那些人相关。通常它会,但这种对称性不是必需的。以爱为例——它是双向的(“我爱”、“爱我”),但通常是不对称的(“我爱她,但她不爱我”)!

All of these are well supported by Hibernate and JPA. Just remember that Hibernate or any other ORM doesn't give a hoot about maintaining symmetry when managing bi-directional many-to-many relationships...thats all up to the application.

所有这些都得到了 Hibernate 和 JPA 的良好支持。请记住,在管理双向多对多关系时,Hibernate 或任何其他 ORM 并没有提及维护对称性……这完全取决于应用程序。

回答by alejandrobog

Take a look at this article: Mapping Object Relationships

看看这篇文章:映射对象关系

There are two categories of object relationships that you need to be concerned with when mapping. The first category is based on multiplicity and it includes three types:

映射时需要关注两类对象关系。第一类是基于多样性的,它包括三种类型:

*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. 

The second category is based on directionality and it contains two types, uni-directional relationships and bi-directional relationships.

第二类是基于方向性的,它包含两种类型,单向关系和双向关系。

*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. 

回答by msshapira

this would probably call for a many-to-many relation ship as follows

这可能需要如下的多对多关系



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
}

you may need to define a joinTable + JoinColumn but it will possible work also without...

您可能需要定义一个 joinTable + JoinColumn 但它也可以在没有...的情况下工作

回答by jyoungdev

First of all, read all the fine print. Note that NHibernate (thus, I assume, Hibernate as well) relational mapping has a funny correspondance with DB and object graph mapping. For example, one-to-one relationships are often implemented as a many-to-one relationship.

首先,阅读所有细则。请注意,NHibernate(因此,我假设 Hibernate 也是如此)关系映射与 DB 和对象图映射有一个有趣的对应关系。例如,一对一关系通常被实现为多对一关系。

Second, before we can tell you how you should write your O/R map, we have to see your DB as well. In particular, can a single Skill be possesses by multiple people? If so, you have a many-to-many relationship; otherwise, it's many-to-one.

其次,在我们告诉您应该如何编写 O/R 映射之前,我们还必须查看您的数据库。尤其是一个技能可以被多人拥有吗?如果是这样,你就有一个多对多的关系;否则,它是多对一的。

Third, I prefer not to implement many-to-many relationships directly, but instead model the "join table" in your domain model--i.e., treat it as an entity, like this:

第三,我不喜欢直接实现多对多关系,而是在域模型中对“连接表”进行建模——即将其视为一个实体,如下所示:

class PersonSkill 
{
    Person person;
    Skill skill;    
}

Then do you see what you have? You have two one-to-many relationships. (In this case, Person may have a collection of PersonSkills, but would not have a collection of Skills.) However, some will prefer to use many-to-many relationship (between Person and Skill); this is controversial.

那你看看你有什么?你有两个一对多的关系。(在这种情况下,Person 可能有一个 PersonSkills 的集合,但不会有一个 Skills 的集合。)但是,有些人会更喜欢使用多对多关系(Person 和 Skill 之间);这是有争议的。

Fourth, if you do have bidirectional relationships (e.g., not only does Person have a collection of Skills, but also, Skill has a collection of Persons), NHibernate does notenforce bidirectionality in your BL for you; it only understands bidirectionality of the relationships for persistence purposes.

第四,如果你有双向关系(例如,不仅人体有一定技能的集合,而且,有技能的人的集合),NHibernate的并没有强制执行的双向您的BL你; 它仅出于持久性目的理解关系的双向性。

Fifth, many-to-one is much easier to use correctly in NHibernate (and I assume Hibernate) than one-to-many (collection mapping).

第五,多对一在 NHibernate(我假设是 Hibernate)中比一对多(集合映射)更容易正确使用。

Good luck!

祝你好运!

回答by jhegedus

1) The circles are Entities/POJOs/Beans

1)圆圈是实体/POJOs/Beans

2) deg is an abbreviation for degree as in graphs (number of edges)

2) deg 是图中度数的缩写(边数)

PK=Primary key, FK=Foreign key

PK=主键,FK=外键

Note the contradiction between the degree and the name of the side. Many corresponds to degree=1 while One corresponds to degree >1.

注意度数和边名的矛盾。许多对应于度= 1,而一个对应于度> 1。

Illustration of one-to-many many-to-one

一对多多对一的插图

回答by Alexander Suraphel

Looks like everyone is answering One-to-manyvs. Many-to-many:

看起来每个人都在回答One-to-manyvs. Many-to-many

The difference between One-to-many, Many-to-oneand Many-to-Manyis:

One-to-many,Many-to-one和之间的区别Many-to-Many是:

One-to-manyvs Many-to-oneis a matter of perspective. Unidirectionalvs Bidirectionalwill not affect the mapping but will make difference on how you can access your data.

One-to-manyvsMany-to-one是一个视角问题UnidirectionalvsBidirectional不会影响映射,但会影响您访问数据的方式。

  • In Many-to-onethe manyside will keep reference of the oneside. A good example is "A State has Cities". In this case Stateis the one side and Cityis the many side. There will be a column state_idin the table cities.
  • Many-to-onemany侧将保持基准one侧。一个很好的例子是“A State has Cities”。在这种情况下State是一侧和City多侧。state_id表格中会有一列cities

In unidirectional, Personclass will have List<Skill> skillsbut Skillwill not have Person person. In bidirectional, both properties are added and it allows you to access a Persongiven a skill( i.e. skill.person).

单向中Person类会有List<Skill> skillsSkill不会有Person person。在双向中,这两个属性都被添加,它允许您访问Person给定的技能(即skill.person)。

  • In One-to-Manythe one side will be our point of reference. For example, "A User has an Addresses". In this case we might have three columns address_1_id, address_2_idand address_3_idor a look up tablewith unique constraint on user_idand address_id.
  • One-to-Many一侧将是我们的参考点。例如,“一个用户有一个地址”。在这种情况下,我们可能有三列address_1_idaddress_2_id和/address_3_id或一个对and具有唯一约束的查找表user_idaddress_id

In unidirectional, a Userwill have Address address. Bidirectionalwill have an additional List<User> usersin the Addressclass.

单向中,aUser会有Address address双向将有一个额外List<User> usersAddress类。

  • In Many-to-Manymembers of each party can hold reference to arbitrary number of members of the other party. To achieve this a look up tableis used. Example for this is the relationship between doctors and patients. A doctor can have many patients and vice versa.
  • Many-to-Many每一方的成员中可以持有任意数量的另一方成员的引用。为了实现这一点,使用了查找表。例如,医生和患者之间的关系。一个医生可以有很多病人,反之亦然。

回答by Duracell De Monaco

I would explain that way:

我会这样解释:

OneToOne - OneToOnerelationship

OneToOne - 一对一关系

@OneToOne
Person person;

@OneToOne
Nose nose;

OneToMany - ManyToOnerelationship

OneToMany - 多对一关系

@OneToMany
Shepherd> shepherd;

@ManyToOne
List<Sheep> sheeps;

ManyToMany - ManyToManyrelationship

多对多 - 多对多关系

@ManyToMany
List<Traveler> travelers;

@ManyToMany
List<Destination> destinations;

回答by Vlad Mihalcea

This is a very common question, so this answer is based on this articleI wrote on my blog.

这是一个很常见的问题,所以这个答案是基于我在博客上写的这篇文章

One-to-many

一对多

The one-to-many table relationship looks as follows:

一对多表关系如下所示:

One-to-many

一对多

In a relational database system, a one-to-many table relationship links two tables based on a Foreign Keycolumn in the child which references the Primary Keyof the parent table row.

在关系数据库系统中,一对多表关系基于Foreign KeyPrimary Key表中引用父表行的列的列链接两个表。

In the table diagram above, the post_idcolumn in the post_commenttable has a Foreign Keyrelationship with the posttable id Primary Keycolumn:

上表post_id中,post_comment表中的列Foreign Keypost表idPrimary Key列有关系:

ALTER TABLE
    post_comment
ADD CONSTRAINT
    fk_post_comment_post_id
FOREIGN KEY (post_id) REFERENCES post

@ManyToOne annotation

@ManyToOne 注释

The best way to map the one-to-many table relationship is to use the @ManyToOneannotation.

映射一对多表关系的最佳方式是使用@ManyToOne注解。

In our case, the child entity, PostCommentmaps the post_idForeign Key column using the @ManyToOneannotation:

在我们的例子中,子实体使用注释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;

}

Using the JPA @OneToManyannotation

使用 JPA@OneToMany注释

Just because you have the option of using the @OneToManyannotation, it does not mean this should be the default option for every one-to-manydatabase relationship. The problem with collections is that we can only use them when the number of child records is rather limited.

仅仅因为您可以选择使用@OneToMany注释,并不意味着这应该是每个一对多数据库关系的默认选项。集合的问题在于我们只能在子记录数量相当有限的情况下使用它们。

The best way to map a @OneToManyassociation is to rely on the @ManyToOneside to propagate all entity state changes:

映射@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);
    }
}

The parent entity, Post, features two utility methods (e.g. addCommentand removeComment) which are used to synchronize both sides of the bidirectional association. You should always provide these methods whenever you are working with a bidirectional association as, otherwise, you risk very subtle state propagation issues.

父实体Post具有两个实用方法(例如addCommentremoveComment),用于同步双向关联的双方。每当您使用双向关联时,您都应该始终提供这些方法,否则,您可能会面临非常微妙的状态传播问题

The unidirectional @OneToManyassociation is to be avoided as it's less efficient than using @ManyToOneor the bidirectional @OneToManyassociation.

@OneToMany应避免单向关联,因为它比使用@ManyToOne或双向@OneToMany关联效率低。

For more details about the best way to map the @OneToManyrelationship with JPA and Hibernate, check out this article.

有关映射@OneToMany与 JPA 和 Hibernate 关系的最佳方式的更多详细信息,请查看这篇文章

One-to-one

一对一

The one-to-one table relationship looks as follows:

一对一的表关系如下所示:

One-to-one

一对一

In a relational database system, a one-to-one table relationship links two tables based on a Primary Keycolumn in the child which is also a Foreign Keyreferencing the Primary Keyof the parent table row.

在关系数据库系统中,一对一的表关系基于Primary Key子表中的列链接两个表,该列也是父表行的Foreign Key引用Primary Key

Therefore, we can say that the child table shares the Primary Keywith the parent table.

因此,我们可以说子表Primary Key与父表共享。

In the table diagram above, the idcolumn in the post_detailstable has also a Foreign Keyrelationship with the posttable idPrimary Keycolumn:

在上面的表格图id中,post_details表格中的列也Foreign Keypost表格idPrimary Key列有关系:

ALTER TABLE
    post_details
ADD CONSTRAINT
    fk_post_details_id
FOREIGN KEY (id) REFERENCES post

Using the JPA @OneToOnewith @MapsIdannotations

使用@OneToOne带有@MapsId注释的 JPA

The best way to map a @OneToOnerelationship is to use @MapsId. This way, you don't even need a bidirectional association since you can always fetch the PostDetailsentity by using the Postentity identifier.

映射@OneToOne关系的最佳方法是使用@MapsId. 这样,您甚至不需要双向关联,因为您始终可以PostDetails使用Post实体标识符获取实体。

The mapping looks like this:

映射如下所示:

[code language="java"] @Entity(name = "PostDetails") @Table(name = "post_details") public class PostDetails {

[code language="java"] @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

} [/code]

} [/代码]

This way, the idproperty serves as both Primary Key and Foreign Key. You'll notice that the @Idcolumn no longer uses a @GeneratedValueannotation since the identifier is populated with the identifier of the postassociation.

这样,该id属性既用作主键又用作外键。您会注意到该@Id列不再使用@GeneratedValue注释,因为标识符填充了post关联的标识符。

For more details about the best way to map the @OneToOnerelationship with JPA and Hibernate, check out this article.

有关映射@OneToOne与 JPA 和 Hibernate 关系的最佳方式的更多详细信息,请查看这篇文章

Many-to-many

多对多

The many-to-many table relationship looks as follows:

多对多表关系如下所示:

Many-to-many

多对多

In a relational database system, a many-to-many table relationship links two parent tables via a child table which contains two Foreign Keycolumns referencing the Primary Keycolumns of the two parent tables.

在关系数据库系统中,多对多表关系通过子表链接两个父表,子表包含Foreign Key引用Primary Key两个父表的列的两列。

In the table diagram above, the post_idcolumn in the post_tagtable has also a Foreign Keyrelationship with the posttable id Primary Keycolumn:

在上面的表图post_id中,post_tag表中的列也Foreign Keypost表idPrimary Key列有关系:

ALTER TABLE
    post_tag
ADD CONSTRAINT
    fk_post_tag_post_id
FOREIGN KEY (post_id) REFERENCES post

And, the tag_idcolumn in the post_tagtable has a Foreign Keyrelationship with the tagtable id Primary Keycolumn:

而且,tag_id在列post_tag表中有一个Foreign Key与关系tag表IDPrimary Key列:

ALTER TABLE
    post_tag
ADD CONSTRAINT
    fk_post_tag_tag_id
FOREIGN KEY (tag_id) REFERENCES tag

Using the JPA @ManyToManymapping

使用 JPA@ManyToMany映射

This is how you can map the many-to-manytable relationship with JPA and Hibernate:

这是您如何many-to-many使用 JPA 和 Hibernate映射表关系:

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

@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);
    }
}
  1. The tagsassociation in the Postentity only defines the PERSISTand MERGEcascade types. As explained in this article, the REMOVEentity state transitiondoesn't make any sense for a @ManyToManyJPA association since it could trigger a chain deletion that would ultimately wipe both sides of the association.
  2. As explained in this article, the add/remove utility methods are mandatory if you use bidirectional associations so that you can make sure that both sides of the association are in sync.
  3. The Postentity uses the entity identifier for equality since it lacks any unique business key. As explained in this article, you can use the entity identifier for equality as long as you make sure that it stays consistent across all entity state transitions.
  4. The Tagentity has a unique business key which is marked with the Hibernate-specific @NaturalIdannotation. When that's the case, the unique business key is the best candidate for equality checks.
  5. The mappedByattribute of the postsassociation in the Tagentity marks that, in this bidirectional relationship, the Postentity owns the association. This is needed since only one side can own a relationship, and changes are only propagated to the database from this particular side.
  6. The Setis to be preferred, as using a Listwith @ManyToManyis less efficient.
  1. 实体中的tags关联Post只定义了PERSISTMERGE级联类型。如本文所述,REMOVE实体状态转换对于@ManyToManyJPA 关联没有任何意义,因为它可能触发链删除,最终会擦除关联的双方。
  2. 本文所述,如果您使用双向关联,则添加/删除实用程序方法是必需的,以便您可以确保关联的双方同步。
  3. Post实体使用的相等实体标识符,因为它没有任何独特的业务重点。如本文所述,只要确保实体标识符在所有实体状态转换中保持一致,就可以使用实体标识符来表示相等
  4. Tag实体具有唯一的业务键,该键标有 Hibernate 特定的@NaturalId注释。在这种情况下,唯一的业务键是平等检查的最佳候选者
  5. 实体中关联的mappedBy属性标志着,在这种双向关系中,实体拥有该关联。这是必需的,因为只有一方可以拥有关系,并且更改仅从这一特定方传播到数据库。postsTagPost
  6. Set是优选的,如使用List具有@ManyToMany效率较低。

For more details about the best way to map the @ManyToManyrelationship with JPA and Hibernate, check out this article.

有关映射@ManyToMany与 JPA 和 Hibernate 关系的最佳方式的更多详细信息,请查看这篇文章