1対多、多対1、多対多の違いは?


144

わかりましたので、これはささいな質問かもしれませんが、違いを視覚化して理解し、それぞれをいつ使用するか理解できません。また、単方向マッピングや双方向マッピングなどの概念が1対多/多対多の関係にどのように影響するかについても、少し不明確です。私は現在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
}

したがって、この場合、どのようなマッピングが必要になりますか?この特定の例に対する回答は間違いなく高く評価されていますが、1対多と多対多のどちらを使用するか、結合テーブルと結合列を使用する場合と単方向と双方向を使用する場合の概要についても教えてください。


11
誰もが1対多と多対多にしか答えていないようです。1対多と多対1の私の答えを見てください。
Alexander Suraphel 2013年

回答:


165

1対多:1人のスキルには多くのスキルがあり、スキルはユーザー間で再利用されません

  • 単方向:セットを介してスキルを直接参照できます
  • 双方向:各「子」スキルには、Person(コードには表示されていません)に戻る単一のポインターがあります。

多対多:1人のスキルには多くのスキルがあり、スキルは複数の人の間で再利用されます

  • 単方向:セットを介してスキルを直接参照できます
  • 双方向:スキルには、それに関連する一連の人がいます。

1対多の関係では、1つのオブジェクトが「親」であり、1つが「子」です。親は子の存在を制御します。多対多では、どちらかのタイプの存在は、両方の外部にあるものに依存します(より大きなアプリケーションコンテキストで)。

主題(ドメイン)は、関係が1対多または多対多のどちらであるかを決定する必要があります-ただし、関係を単方向または双方向にすることは、メモリ、処理、パフォーマンスのトレードオフとなるエンジニアリング上の決定であることがわかりました、など

紛らわしいのは、多対多の双方向関係が対称である必要がないことです。つまり、多数のPeopleがスキルをポイントする可能性がありますが、スキルはそれらのユーザーだけに関係する必要はありません。通常はそうですが、そのような対称性は必須ではありません。たとえば、愛を取り上げます-双方向ですが(「I-Love」、「Loves-Me」)、しばしば非対称です(「私は彼女を愛していますが、彼女は私を愛していません」)。

これらはすべてHibernateとJPAによって十分にサポートされています。Hibernateやその他のORMは、双方向の多対多の関係を管理する際に対称性を維持することについて面倒を与えないことを覚えておいてください...すべてはアプリケーション次第です。


明確にするために、BLまたはO / Rマッピングでは、相互関係は一方向または双方向である可能性があります(相互に独立している場合もあります)。
jyoungdev

4
「LOVE」の例はそれを明確にしただけです。ManyToManyは私のタイプのマッピングです。
Abdullah Khan

1
素晴らしい。これはそれを非常によく説明します(OPの例との関連で)
Anupam

1
質問に正しく答えません。あなたは多くの部分を欠場します。1対多および多対1は知覚の問題ですが、この回答ではそれについて言及していません。
Manzur Alahi

248

皆のように見えるが応答しているOne-to-many対をMany-to-many

違いOne-to-manyMany-to-oneそしてMany-to-Many次のとおりです。

One-to-manyvs Many-to-oneは視点の問題ですUnidirectionalvs Bidirectionalはマッピングには影響しませんが、データへのアクセス方法に違いをもたらします。

  • では側の参照続けるサイドを。良い例は「州には都市がある」です。この場合は片側であり、多側です。テーブルに列があります。Many-to-onemanyoneStateCitystate_idcities

単方向PersonクラスがありますList<Skill> skillsが、 Skill持っていませんPerson person。では、双方向、両方のプロパティが追加され、それはあなたがアクセスすることを可能にするPerson技術(すなわち与えられましたskill.person)。

  • 一方でOne-to-Manyは、私たちの参照ポイントになります。たとえば、「ユーザーはアドレスを持っています」などです。このケースでは、3つの列かもしれないaddress_1_idaddress_2_idaddress_3_idか、テーブルのルックアップユニーク上の制約を持つuser_idとしますaddress_id

では、単方向User持っていますAddress address双方向List<User> usersAddressクラスに追加があります。

  • Many-to-Many各当事者のメンバー相手のメンバーの任意の数の参照を保持することができます。これを実現するために、ルックアップテーブルが使用されます。この例は、医師と患者の関係です。医師は多くの患者を持つことができ、逆もまた同様です。

27
これは受け入れられる答えであるはずです。他のほとんどの答えは質問を逃しています。
arg20 2014年

最初の例は正しくありません。Personがあり、Personにスキルを持つ@OneToManyがある場合、そのテーブルpreson_skillsには​​skill_idに一意の制約があります。したがって、1つのスキルは1人にのみマップされます。そして、あなたは抽出することはできませんs.personsだけがあるので、s.person
ИванНиколайчук

実際にOne-to-manyあなたが説明するような関係があるMany-to-manyので、関係personの多くへの参照を持っているskillsが、skill特定の人への参照を保持していないと、多くのpersons参照を同じに持つことができますskill。そして、あなたのMany-to-one関係は実際には、子供には母親がOne-to-many1人しかいないので、各スキルには1つだけの参照があるためpersonです。
ミクセル2017

@mixelあなたのコメントは結局私に私の答えのほとんどを書き直させました。是非チェックしてください!ありがとう
Alexander Suraphel

1対多と多対1は、「1」側の重要性によって区別されます。どちらの例でも、「ユーザー」が最も重要なエンティティです。つまり、それが "One"(ユーザーとスキル、skills持つperson_id)の場合は、1対多と呼びますが、 "多"側(ユーザーとアドレス、users持つaddress_id)の場合は、多対1と呼びます。ただし、構造的には両方のケースが同一であり、1対多と呼ばれます。
ミクセル2017年

38

1)円はエンティティ/ POJO / Beanです

2)度は、グラフと同様に度数の略語(エッジの数)です。

PK =主キー、FK =外部キー

次数と側面の名前の矛盾に注意してください。多くは次数= 1に対応し、一方は次数> 1に対応します。

1対多の多対1のイラスト


1
オブジェクトグラフをテーブルに双方向で結び付ける方法が大好きです。
Dmitry Minkovsky

3
ルックオタクが、これはどのようにプログラマの 手書きのように見える:D
Mehrajマリク

ここで何をしたかわかります。
Nick Gallimore

8

この記事を見てください:オブジェクト関係のマッピング

マッピングの際に注意する必要があるオブジェクトの関係には、2つのカテゴリがあります。最初のカテゴリは多重度に基づいており、次の3つのタイプが含まれます。

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

2番目のカテゴリは方向性に基づいており、単方向の関係と双方向の関係の2つのタイプが含まれます。

*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 one笑った?
sergは、

3

これは非常に一般的な質問なので、この回答は私がブログに書いたこの記事に基づいています。

1対多

1対多のテーブルの関係は次のようになります。

1対多

リレーショナルデータベースシステムでは、1対多のテーブルリレーションシップは、親テーブル行のForeign Keyを参照する子の列に基づいて2つのテーブルをリンクしますPrimary Key

上記の表の図では、post_idpost_commentテーブルが有しているForeign Keyとの関係postテーブルID Primary Keyカラム:

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

@ManyToOneアノテーション

1対多のテーブルの関係をマッピングする最良の方法は、@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アノテーションを使用するオプションがあるからといって、これがすべての1対多のデータベース関係のデフォルトオプションであることを意味するわけではありません。コレクションの問題は、子レコードの数がかなり限られている場合にのみ使用できることです。

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

親エンティティは、双方向の関連付けの両側を同期するために使用されるPost2つのユーティリティメソッド(addCommentおよびなどremoveComment)を備えています。双方向の関連付けを使用しているときは常に、これらのメソッドを常に提供する必要があります。そうしないと、非常に微妙な状態伝播の問題が発生するおそれがあります

単方向の@OneToMany関連付けは、を使用し@ManyToOneたり、双方向の@OneToMany関連付けよりも効率が悪いため、避けてください。

@OneToManyJPAおよびHibernateとの関係をマッピングする最良の方法について詳しくは、こちらの記事をご覧ください。

1対1

1対1のテーブルの関係は次のようになります。

1対1

リレーショナルデータベースシステムでは、1対1のテーブルリレーションシップは、親テーブル行の参照Primary Keyでもある子の列に基づいて2つのテーブルをリンクします。Foreign KeyPrimary Key

したがって、子テーブルPrimary Keyは親テーブルと共有していると言えます。

上記の表の図では、idpost_detailsテーブルも有しているForeign Keyとの関係postテーブルのid Primary Key列を:

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

JPAを使用し@OneToOne@MapsId注釈

@OneToOne関係をマッピングする最良の方法は、を使用すること@MapsIdです。この方法ではPostDetailsPostエンティティ識別子を使用してエンティティをいつでもフェッチできるため、双方向の関連付けも必要ありません。

マッピングは次のようになります。

[code language = "java"] @Entity(name = "PostDetails")@Table(name = "post_details")パブリッククラス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列で@GeneratedValueアノテーションが使用されなくなっていることがわかりpostます。

@OneToOneJPAおよびHibernateとの関係をマッピングする最良の方法について詳しくは、こちらの記事をご覧ください。

多対多

多対多のテーブルの関係は次のようになります。

多対多

リレーショナルデータベースシステムでは、多対多のテーブルリレーションシップは、2つの親テーブルのForeign Key列を参照する2つの列を含む子テーブルを介して2つの親テーブルをリンクしますPrimary Key

上記の表の図では、post_idpost_tagテーブルも有しているForeign Keyとの関係postテーブルID Primary Keyカラム:

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

そして、tag_idpost_tagの表では、持っているForeign Keyとの関係tagテーブルのid Primary Key列を:

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

JPA @ManyToManyマッピングの使用

これは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. エンティティのtags関連付けPostは、PERSISTおよびMERGEカスケードタイプのみを定義します。この記事で説明したように、REMOVE エンティティの状態遷移@ManyToMany、最終的には関連付けの両側をワイプするチェーンの削除をトリガーする可能性があるため、JPAの関連付けには意味がありません。
  2. この記事で説明するように、双方向の関連付けを使用する場合、関連付けの両側が確実に同期するように、追加/削除ユーティリティのメソッドは必須です。
  3. Postそれがどんなユニークなビジネスキーを欠いので、エンティティは、平等のためのエンティティの識別子を使用しています。この記事で説明されているように、エンティティ識別子は、すべてのエンティティの状態遷移で一貫性が保たれていることを確認する限り、同等に使用できます。
  4. Tagエンティティは、Hibernate固有のでマークされたユニークなビジネスキーがある@NaturalId注釈を。その場合、一意のビジネスキーが同等性チェックの最適な候補になります
  5. エンティティmappedByposts関連付けの属性は、Tagこの双方向の関係で、Postエンティティが関連付けを所有していることを示します。これが必要なのは、関係を所有できるのは片側だけであり、変更はこの特定の側からのみデータベースに伝搬されるためです。
  6. SetAを使用するものとして、好ましいことであるList@ManyToMany効率が低いです。

@ManyToManyJPAおよびHibernateとの関係をマッピングする最良の方法について詳しくは、こちらの記事をご覧ください。


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を定義する必要があるかもしれませんが、それなしでも可能です...


1

私はそのように説明します:

OneToOne-OneToOne関係

@OneToOne
Person person;

@OneToOne
Nose nose;

OneToMany-ManyToOne関係

@OneToMany
Shepherd> shepherd;

@ManyToOne
List<Sheep> sheeps;

ManyToMany-ManyToMany関係

@ManyToMany
List<Traveler> travelers;

@ManyToMany
List<Destination> destinations;

0

まず、すべての細かい活字を読みます。NHibernate(したがって、私はHibernateも想定しています)リレーショナルマッピングは、DBおよびオブジェクトグラフマッピングとおかしな対応関係にあることに注意してください。たとえば、1対1の関係は、多対1の関係として実装されることがよくあります。

次に、O / Rマップの記述方法を説明する前に、DBも確認する必要があります。特に、単一のスキルを複数の人が所有することはできますか?もしそうなら、あなたは多対多の関係を持っています。それ以外の場合は、多対1です。

3番目に、多対多の関係を直接実装するのではなく、ドメインモデルで「結合テーブル」をモデル化します。つまり、次のようにエンティティとして扱います。

class PersonSkill 
{
    Person person;
    Skill skill;    
}

その後、あなたはあなたが持っているものを見ますか?2つの1対多の関係があります。(この場合、PersonにはPersonSkillsのコレクションがありますが、Skillsのコレクションはありません。)ただし、多対多の関係(PersonとSkillの間)の使用を好む人もいます。これは物議を醸しています。

4番目に、双方向の関係がある場合(たとえば、Personにスキルのコレクションがあるだけでなく、SkillにもPersonのコレクションがある)、NHibernateはBLに双方向性を強制しませ。永続化の目的で関係の双方向性のみを理解します。

5番目に、多対1(コレクションマッピング)よりも、NHibernate(および私はHibernateを想定)で多対1を使用する方がはるかに簡単です。

幸運を!

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.