Sự khác biệt giữa một-nhiều, nhiều-nhiều và nhiều-nhiều?


144

Ok, đây có lẽ là một câu hỏi nhỏ nhưng tôi gặp khó khăn trong việc hình dung và hiểu sự khác biệt và khi nào nên sử dụng chúng. Tôi cũng không rõ ràng về cách các khái niệm như ánh xạ hai chiều và hai chiều ảnh hưởng đến mối quan hệ một-nhiều / nhiều-nhiều. Tôi đang sử dụng Hibernate ngay bây giờ vì vậy mọi lời giải thích liên quan đến ORM sẽ hữu ích.

Để làm ví dụ, hãy nói rằng tôi có thiết lập sau:

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
}

Vì vậy, trong trường hợp này tôi sẽ có loại ánh xạ nào? Câu trả lời cho ví dụ cụ thể này chắc chắn được đánh giá cao nhưng tôi cũng thực sự muốn có một cái nhìn tổng quan về thời điểm nên sử dụng một-nhiều và nhiều-nhiều và khi nào nên sử dụng bảng tham gia so với cột tham gia và một chiều so với hai chiều.


11
Hình như mọi người chỉ đang trả lời Một-nhiều-nhiều-nhiều-nhiều. Hãy xem câu trả lời của tôi cho One-to-many vs Many-to-one.
Alexander Suraphel

Câu trả lời:


165

Một-nhiều : Một người có nhiều kỹ năng, một kỹ năng không được sử dụng lại giữa các người

  • Đơn hướng : Một người có thể trực tiếp tham khảo các Kỹ năng thông qua Tập hợp của mình
  • Hai chiều : Mỗi Kỹ năng "con" có một con trỏ duy nhất sao lưu vào Người (không được hiển thị trong mã của bạn)

Nhiều-nhiều : Một người có nhiều kỹ năng, một kỹ năng được sử dụng lại giữa (các) người

  • Đơn hướng : Một người có thể trực tiếp tham khảo các Kỹ năng thông qua Tập hợp của mình
  • Hai chiều : Một kỹ năng có một nhóm người liên quan đến nó.

Trong mối quan hệ Một-Nhiều, một đối tượng là "cha mẹ" và một đối tượng là "con". Cha mẹ kiểm soát sự tồn tại của đứa trẻ. Trong Nhiều-Nhiều-nhiều, sự tồn tại của một trong hai loại phụ thuộc vào một cái gì đó bên ngoài cả hai (trong bối cảnh ứng dụng lớn hơn).

Đối tượng (miền) của bạn nên ra lệnh cho dù mối quan hệ đó là Một-Nhiều hay Nhiều-Nhiều - tuy nhiên, tôi thấy rằng làm cho mối quan hệ một chiều hoặc hai chiều là một quyết định kỹ thuật đánh đổi bộ nhớ, xử lý, hiệu suất , Vân vân.

Điều có thể gây nhầm lẫn là mối quan hệ Nhiều-Nhiều-nhiều không cần phải đối xứng! Đó là, một nhóm người có thể chỉ ra một kỹ năng, nhưng kỹ năng đó không cần liên quan đến chỉ những người đó. Thông thường nó sẽ, nhưng đối xứng như vậy không phải là một yêu cầu. Lấy tình yêu, ví dụ - đó là hai chiều ("Tôi yêu", "Yêu-tôi"), nhưng thường không đối xứng ("Tôi yêu cô ấy, nhưng cô ấy không yêu tôi")!

Tất cả những thứ này đều được Hibernate và JPA hỗ trợ tốt. Chỉ cần nhớ rằng Hibernate hoặc bất kỳ ORM nào khác không có ý nghĩa gì về việc duy trì tính đối xứng khi quản lý các mối quan hệ nhiều-nhiều-hai hướng ... tất cả đều phụ thuộc vào ứng dụng.


Để làm rõ, bất kỳ mối quan hệ nào cũng có thể là một hướng hoặc hai chiều trong BL hoặc trong ánh xạ O / R của bạn (độc lập với nhau, thậm chí!).
jyoungdev

4
Ví dụ "TÌNH YÊU" chỉ làm rõ nó. ManyToMany là loại bản đồ của tôi.
Abdullah Khan

1
Siêu. Điều này giải thích điều đó rất tốt (và trong bối cảnh ví dụ của OP)
Anupam

1
Không trả lời đúng câu hỏi. bạn bỏ lỡ nhiều đến một phần. mặc dù một đến nhiều và nhiều với nhau là vấn đề về nhận thức, câu trả lời này không đề cập đến điều đó.
Manzur Alahi

248

Hình như mọi người đang trả lời One-to-manyso với Many-to-many:

Sự khác biệt giữa One-to-many, Many-to-oneMany-to-Manylà:

One-to-manyvs Many-to-onelà một vấn đề của quan điểm . Unidirectionalvs Bidirectionalsẽ không ảnh hưởng đến ánh xạ nhưng sẽ tạo ra sự khác biệt về cách bạn có thể truy cập dữ liệu của mình.

  • Trong Many-to-one các manybên sẽ tiếp tục tham khảo của onephụ. Một ví dụ điển hình là "Một bang có thành phố". Trong trường hợp Statenày là một bên và Citylà nhiều bên. Sẽ có một cột state_idtrong bảng cities.

Trong một chiều , Personlớp sẽ có List<Skill> skillsnhưng Skill sẽ không có Person person. Trong hai chiều , cả hai thuộc tính đều được thêm vào và nó cho phép bạn truy cập Personmột kỹ năng nhất định (nghĩa là skill.person).

  • Trong One-to-Manymột bên sẽ là điểm tham chiếu của chúng tôi. Ví dụ: "Người dùng có Địa chỉ". Trong trường hợp này, chúng tôi có thể có ba cột address_1_id,address_2_idaddress_3_idhoặc một cái nhìn lên bảng với chế duy nhất trên user_idaddress_id.

Trong một chiều , mộtUser ý chí có Address address. Hai chiều sẽ có một bổ sung List<User> userstrong Addresslớp.

  • Trong Many-to-Manycác thành viên của mỗi bên có thể giữ tham chiếu đến số lượng thành viên tùy ý của bên kia. Để đạt được điều này một bảng tra cứu được sử dụng. Ví dụ cho điều này là mối quan hệ giữa bác sĩ và bệnh nhân. Một bác sĩ có thể có nhiều bệnh nhân và ngược lại.

27
đây phải là câu trả lời được chấp nhận, hầu hết các câu trả lời khác đều bỏ lỡ câu hỏi.
arg20

Ví dụ đầu tiên là không chính xác. Nếu bạn có Người và Người có @OneToMany có Kỹ năng, bảng đó preson_skills sẽ có một ràng buộc duy nhất đối với skill_id. Vì vậy, một kỹ năng sẽ chỉ được ánh xạ cho một người. Và bạn không thể giải nén s.persons, như chỉ cós.person
Иван Николайчук

Thực tế One-to-manymối quan hệ như bạn mô tả là Many-to-manymối quan hệ vì personcó một tham chiếu đến nhiều người skillsnhưng skillkhông giữ tham chiếu đến một người cụ thể và nhiều người personscó thể có một tài liệu tham khảo giống nhau skill. Và Many-to-onemối quan hệ của bạn thực sự là One-to-manybởi vì mỗi kỹ năng chỉ có một tham chiếu đến personmột đứa trẻ chỉ có một người mẹ.
mixel

@mixel bình luận của bạn đã kết thúc khiến tôi viết lại hầu hết câu trả lời của tôi. Vui lòng kiểm tra lại! Cảm ơn
Alexander Suraphel

Bạn phân biệt giữa một-nhiều và nhiều-một theo ý nghĩa của bên "Một". Trong cả hai ví dụ "Người dùng" là thực thể quan trọng nhất. Vì vậy, khi nó vào "One" (người sử dụng và kỹ năng, skillsperson_id) sau đó bạn gọi nó là One-to-many nhưng khi đó là một trong những "Nhiều" bên (người dùng và địa chỉ, usersaddress_id) sau đó bạn gọi nó Many-to-one. Nhưng về mặt cấu trúc, cả hai trường hợp đều giống hệt nhau và được gọi là Một-nhiều.
mixel

38

1) Các vòng tròn là Thực thể / POJOs / Đậu

2) deg là tên viết tắt của độ như trong biểu đồ (số cạnh)

PK = Khóa chính, FK = Khóa ngoại

Lưu ý mâu thuẫn giữa mức độ và tên của bên. Nhiều tương ứng với độ = 1 trong khi Một tương ứng với độ> 1.

Minh họa của một-nhiều-nhiều-một


1
Thực sự thích cách nó liên kết biểu đồ đối tượng với các bảng theo cả hai hướng.
Dmitry Minkovsky

3
Nhìn nerds, đây là cách TÌM HIỂU CỦA CHƯƠNG TRÌNH trông như thế nào: D
Mehraj Malik

Tôi thấy những gì bạn đã làm ở đây.
Nick Gallolas

8

Hãy xem bài viết này: Lập bản đồ Mối quan hệ Đối tượng

Có hai loại mối quan hệ đối tượng mà bạn cần quan tâm khi ánh xạ. Danh mục đầu tiên dựa trên bội số và nó bao gồm ba loại:

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

Loại thứ hai dựa trên tính định hướng và nó chứa hai loại, mối quan hệ đơn hướng và mối quan hệ hai chiều.

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

2
this occurs when the maximum of one multiplicity is one and the other is greater than onehả?
serg

3

Đây là một câu hỏi rất phổ biến, vì vậy câu trả lời này dựa trên bài viết này tôi đã viết trên blog của mình.

Một-nhiều

Mối quan hệ một-nhiều bảng trông như sau:

Một-nhiều

Trong một hệ thống cơ sở dữ liệu quan hệ, mối quan hệ một-nhiều bảng liên kết hai bảng dựa trên một Foreign Keycột trong con tham chiếu đến hàng Primary Keycủa bảng cha.

Trong sơ đồ bảng ở trên, post_idcột trong post_commentbảng có Foreign Keymối quan hệ với cột postid bảng Primary Key:

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

Chú thích @ManyToOne

Cách tốt nhất để ánh xạ mối quan hệ một-nhiều bảng là sử dụng @ManyToOnechú thích.

Trong trường hợp của chúng tôi, thực thể con, PostCommentánh xạ post_idcột Khóa ngoài bằng cách sử dụng @ManyToOnechú thích:

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

}

Sử dụng @OneToManychú thích JPA

Chỉ vì bạn có tùy chọn sử dụng @OneToManychú thích, điều đó không có nghĩa đây sẽ là tùy chọn mặc định cho mỗi một-nhiều mối quan hệ cơ sở dữ liệu . Vấn đề với các bộ sưu tập là chúng tôi chỉ có thể sử dụng chúng khi số lượng hồ sơ con khá hạn chế.

Cách tốt nhất để lập bản đồ @OneToManyliên kết là dựa vào một @ManyToOnebên để tuyên truyền tất cả các thay đổi trạng thái thực thể:

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

Thực thể mẹ Post, có hai phương thức tiện ích (ví dụ addCommentremoveComment) được sử dụng để đồng bộ hóa cả hai mặt của liên kết hai chiều. Bạn nên luôn luôn cung cấp các phương thức này bất cứ khi nào bạn làm việc với một hiệp hội hai chiều vì nếu không, bạn có nguy cơ gặp phải các vấn đề lan truyền trạng thái rất tinh tế .

Nên @OneToManytránh liên kết đơn hướng vì nó kém hiệu quả hơn so với sử dụng @ManyToOnehoặc @OneToManyliên kết hai chiều .

Để biết thêm chi tiết về cách tốt nhất để lập bản đồ @OneToManymối quan hệ với JPA và Hibernate, hãy xem bài viết này .

Một chọi một

Mối quan hệ một-một-bảng trông như sau:

Một chọi một

Trong một hệ thống cơ sở dữ liệu quan hệ, mối quan hệ một-một liên kết hai bảng dựa trên một Primary Keycột trong con cũng là Foreign Keytham chiếu Primary Keycủa hàng của bảng cha.

Do đó, chúng ta có thể nói rằng bảng con chia sẻ Primary Keyvới bảng cha.

Trong sơ đồ bảng trên, idcột trong post_detailsbảng cũng có Foreign Keymối quan hệ với cột postbảng id Primary Key:

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

Sử dụng JPA @OneToOnevới @MapsIdcác chú thích

Cách tốt nhất để lập bản đồ @OneToOnemối quan hệ là sử dụng @MapsId. Theo cách này, bạn thậm chí không cần liên kết hai chiều vì bạn luôn có thể tìm nạp PostDetailsthực thể bằng cách sử dụng Postđịnh danh thực thể.

Ánh xạ trông như thế này:

[code ngôn ngữ = "java"] @Entity (name = "PostDetails") @Table (name = "post_details") lớp công khai 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

} [/ mã]

Bằng cách này, idtài sản đóng vai trò là Khóa chính và Khóa ngoại. Bạn sẽ nhận thấy rằng @Idcột không còn sử dụng @GeneratedValuechú thích vì mã định danh được điền với mã định danh của postliên kết.

Để biết thêm chi tiết về cách tốt nhất để lập bản đồ @OneToOnemối quan hệ với JPA và Hibernate, hãy xem bài viết này .

Nhiều nhiều

Mối quan hệ nhiều-nhiều-bảng trông như sau:

Nhiều nhiều

Trong một hệ thống cơ sở dữ liệu quan hệ, mối quan hệ nhiều-nhiều bảng liên kết hai bảng cha thông qua một bảng con chứa hai Foreign Keycột tham chiếu các Primary Keycột của hai bảng cha.

Trong sơ đồ bảng ở trên, post_idcột trong post_tagbảng cũng có Foreign Keymối quan hệ với cột postid bảng Primary Key:

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

Và, tag_idcột trong post_tagbảng có Foreign Keymối quan hệ với cột tagid bảng Primary Key:

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

Sử dụng @ManyToManyánh xạ JPA

Đây là cách bạn có thể ánh xạ many-to-manymối quan hệ bảng với JPA và 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. Liên tagskết trong Postthực thể chỉ định nghĩa các loại PERSISTMERGEtầng. Như đã giải thích trong bài viết này , REMOVE quá trình chuyển đổi trạng thái thực thể không có ý nghĩa gì đối với một @ManyToManyhiệp hội JPA vì nó có thể kích hoạt việc xóa chuỗi mà cuối cùng sẽ xóa sạch cả hai mặt của hiệp hội.
  2. Như đã giải thích trong bài viết này , các phương thức tiện ích thêm / xóa là bắt buộc nếu bạn sử dụng các liên kết hai chiều để bạn có thể đảm bảo rằng cả hai mặt của liên kết đều đồng bộ.
  3. Thực Postthể sử dụng định danh thực thể cho sự bình đẳng vì nó không có bất kỳ khóa kinh doanh duy nhất nào. Như đã giải thích trong bài viết này , bạn có thể sử dụng định danh thực thể cho sự bình đẳng miễn là bạn đảm bảo rằng nó vẫn nhất quán trong tất cả các chuyển đổi trạng thái thực thể .
  4. Thực Tagthể có khóa doanh nghiệp duy nhất được đánh dấu bằng @NaturalIdchú thích dành riêng cho Hibernate . Khi đó, khóa kinh doanh duy nhất là ứng cử viên tốt nhất để kiểm tra bình đẳng .
  5. Các mappedBythuộc tính của postshiệp hội trong Tagdấu ngoặc đơn vị rằng, trong mối quan hệ hai chiều này, các Postđơn vị sở hữu hiệp hội. Điều này là cần thiết vì chỉ một bên có thể sở hữu một mối quan hệ và các thay đổi chỉ được truyền đến cơ sở dữ liệu từ phía cụ thể này.
  6. Điều Setnày được ưu tiên, vì sử dụng một Listvới @ManyToManyít hiệu quả hơn.

Để biết thêm chi tiết về cách tốt nhất để lập bản đồ @ManyToManymối quan hệ với JPA và Hibernate, hãy xem bài viết này .


1

điều này có thể sẽ gọi cho một tàu quan hệ nhiều-nhiều như sau



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
}

bạn có thể cần xác định một JoinTable + JoinColumn nhưng nó cũng có thể hoạt động mà không cần ...


1

Tôi sẽ giải thích theo cách đó:

Mối quan hệ OneToOne - OneToOne

@OneToOne
Person person;

@OneToOne
Nose nose;

Mối quan hệ OneToMany - ManyToOne

@OneToMany
Shepherd> shepherd;

@ManyToOne
List<Sheep> sheeps;

ManyToMany - Mối quan hệ ManyToMany

@ManyToMany
List<Traveler> travelers;

@ManyToMany
List<Destination> destinations;

0

Trước hết, đọc tất cả các bản in tốt. Lưu ý rằng ánh xạ quan hệ NHibernate (do đó, tôi giả sử, Hibernate cũng vậy) có một sự tương ứng hài hước với DB và ánh xạ đồ thị đối tượng. Ví dụ, mối quan hệ một-một thường được thực hiện như một mối quan hệ nhiều-một.

Thứ hai, trước khi chúng tôi có thể cho bạn biết bạn nên viết bản đồ O / R như thế nào, chúng tôi cũng phải xem DB của bạn. Cụ thể, một Skill có thể được sở hữu bởi nhiều người không? Nếu vậy, bạn có một mối quan hệ nhiều-nhiều; mặt khác, nó là nhiều-một.

Thứ ba, tôi không muốn trực tiếp thực hiện nhiều mối quan hệ nhiều-nhiều mà thay vào đó mô hình hóa "bảng tham gia" trong mô hình miền của bạn - nghĩa là coi nó như một thực thể, như thế này:

class PersonSkill 
{
    Person person;
    Skill skill;    
}

Sau đó, bạn có thấy những gì bạn có? Bạn có hai mối quan hệ một-nhiều. (Trong trường hợp này, Person có thể có bộ sưu tập PersonSkills, nhưng sẽ không có bộ sưu tập Kỹ năng.) Tuy nhiên, một số người sẽ thích sử dụng mối quan hệ nhiều-nhiều (giữa Người và Kỹ năng); điều này gây tranh cãi.

Thứ tư, nếu bạn có mối quan hệ hai chiều (ví dụ: Người không chỉ có bộ sưu tập Kỹ năng mà còn có Kỹ năng có bộ sưu tập Người), NHibernate không áp dụng tính hai chiều trong BL cho bạn; nó chỉ hiểu tính hai chiều của các mối quan hệ cho mục đích bền bỉ.

Thứ năm, nhiều-một-một dễ sử dụng chính xác hơn trong NHibernate (và tôi giả sử Hibernate) so với một-nhiều (ánh xạ bộ sưu tập).

Chúc may mắn!

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.