一对多,多对一和多对多的区别?

时间:2010-06-24 21:08:10

标签: hibernate orm many-to-many one-to-many

好的,这可能是一个微不足道的问题,但我在查看和理解差异以及何时使用每个问题时遇到了麻烦。关于单向和双向映射等概念如何影响一对多/多对多关系,我也有点不清楚。我现在正在使用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
}

那么在这种情况下我会有什么样的映射?这个具体示例的答案肯定是值得赞赏的,但我也非常希望概述何时使用一对多和多对多以及何时使用连接表与连接列以及单向与双向。

8 个答案:

答案 0 :(得分:190)

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

One-to-manyMany-to-oneMany-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行共享。在这种情况下,我们说usersaddressesMany-to-one关系。
  

单向中,UserAddress address双向   List<User> users课程中会有额外的Address

  • Many-to-Many成员中,每一方都可以引用另一方的任意数量的成员。为实现此目的,使用了look up table。这方面的例子是医生和患者之间的关系。医生可以有很多病人,反之亦然。

答案 1 :(得分:140)

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

  • 单向:一个人可以通过其Set
  • 直接引用技能
  • 双向:每个“子”技能都有一个指针备份到 人(未在您的代码中显示)

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

  • 单向:一个人可以通过其Set
  • 直接引用技能
  • 双向:技能有一组与之相关的人。

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

你的主题(领域)应该决定这种关系是一对多还是多对多 - 然而,我发现使关系单向或双向是一种交换记忆的工程决策,处理,表现等。

可能令人困惑的是,多对多双向关系不需要是对称的!也就是说,一群人可以指出一项技能,但技能不需要仅涉及那些人。通常它会,但这种对称性不是必需的。以爱为例 - 它是双向的(“我爱”,“爱我”),但往往是不对称的(“我爱她,但她不爱我”)!

所有这些都得到Hibernate和JPA的很好支持。请记住,在管理双向多对多关系时,Hibernate或任何其他ORM都没有提供关于保持对称性的信息......这完全取决于应用程序。

答案 2 :(得分:33)

1)圈子是实体/ POJO /豆子

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

PK =主键,FK =外键

注意程度和名称之间的矛盾。许多对应于度= 1,而一个对应于度> 1。

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

答案 3 :(得分:11)

这是一个非常常见的问题,因此此答案基于我在博客上写的this article

一对多

一对多表关系如下:

One-to-many

在关系数据库系统中,一对多表关系基于子表中的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

@ManyToOne注释

在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;
        
    }

使用JPA @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实体具有两个实用程序方法(例如addCommentremoveComment),用于同步双向关联的两端。

无论何时使用双向关联,都应提供这些方法,否则将冒very subtle state propagation issues的风险。

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

有关映射@OneToMany与JPA和Hibernate关系的最佳方法的更多详细信息,请查看this article

一对一

一对一的表关系如下:

One-to-one

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

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

在上面的表图中,id表中的post_details列也与Foreign Keypost {{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

多对多

多对多表关系如下所示:

Many-to-many

在关系数据库系统中,多对多表关系通过一个子表链接两个父表,该子表包含引用两个父表的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

使用JPA Primary Key映射

这是您可以通过JPA和Hibernate映射 ALTER TABLE post_tag ADD CONSTRAINT fk_post_tag_tag_id FOREIGN KEY (tag_id) REFERENCES tag 表关系的方法:

@ManyToMany
  1. 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); } } 关联仅定义tagsPost级联类型。正如this article中所述,PERSIST entity state transition对于MERGE JPA关联没有任何意义,因为它可能会触发一条链删除操作,从而最终抹去连接的两面。关联。
  2. this article中所述,如果您使用双向关联,则添加/删除实用程序方法是强制性的,这样可以确保关联的两端同步。
  3. REMOVE实体将实体标识符用于相等性,因为它缺少任何唯一的业务密钥。如this article中所述,只要确保实体标识符在所有entity state transitions上都保持一致,就可以使用实体标识符。
  4. @ManyToMany实体具有唯一的业务密钥,该业务密钥带有特定于Hibernate的Post批注。在这种情况下,唯一的业务密钥为the best candidate for equality checks
  5. Tag实体中@NaturalId关联的mappedBy属性表示,在这种双向关系中,posts实体拥有该关联。这是必需的,因为只有一方可以拥有关系,并且更改仅从该特定方传播到数据库。
  6. 最好使用Tag,因为将PostSet一起使用效率较低。

有关与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;