Різниця між «один-багато-багато», «багато-до-одного» та «багато-багато-багато»?


144

Гаразд, це, мабуть, тривіальне питання, але у мене виникають проблеми з візуалізацією та розумінням відмінностей та коли їх використовувати. Мені також трохи незрозуміло, як такі поняття, як односпрямоване та двонаправлене відображення, впливають на відносини один-багато-багато-багато-багато. Я зараз використовую сплячку, тому будь-яке пояснення, пов’язане з 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
}

Тож у такому випадку яке б картографування я мав би? Відповіді на цей конкретний приклад, безумовно, вдячні, але я також дуже хотів би ознайомитись із тим, коли використовувати або один-багато-багато-багато-багато-багато, і коли використовувати таблицю приєднання проти стовпця приєднання та односпрямованого проти двонаправленого.


11
Схоже, всі відповідають лише «Один до багатьох» проти «Багато-до-багатьох». Подивіться на мою відповідь "Один на багато проти багатьох".
Олександр Сурафель

Відповіді:


165

Один для багатьох : одна людина має багато вмінь, навичка не використовується повторно між особами

  • Однонаправлений : Людина може безпосередньо посилатися на Навички через свій Набір
  • Двонаправлена : Кожна "дочірня" навичка має один покажчик назад до Особи (який не вказаний у вашому коді)

Багато-багато-багато : одна людина має багато вмінь, вміння повторно використовується між особами

  • Однонаправлений : Людина може безпосередньо посилатися на Навички через свій Набір
  • Двонаправлена : Навичка має набір осіб, які відносяться до неї.

У відносинах "Один до багатьох" один об'єкт - "батько", а один - "дитина". Батько контролює існування дитини. У множинних для багатьох існування будь-якого типу залежить від чогось поза ними обох (у більш широкому контексті програми).

Ваша тема (домен) повинна диктувати, чи відносини є "Один до багатьох" або "Багато хто" - однак я вважаю, що прийняття відносин однонаправленим або двонаправленим - це інженерне рішення, яке торгує пам'яттю, обробкою, продуктивністю тощо.

Що може збивати з пантелику те, що двонаправлені відносини «багато-багато-багато» не повинні бути симетричними! Тобто, купа людей може вказувати на майстерність, але майстерність не повинна стосуватися лише тих людей. Зазвичай це було б, але така симетрія не є вимогою. Візьмемо, наприклад, любов - вона двостороння («Я-люблю», «Любить-я»), але часто асиметрична («Я її люблю, але вона мене не любить»)!

Все це добре підтримується в режимі глибокого сну та JPA. Пам'ятайте лише, що сплячий або будь-який інший ORM не дає змоги підтримувати симетрію при керуванні двонаправленими відносинами «багато на багато» ... це все залежно від програми.


Для уточнення, будь-яке відношення може бути одно- або двонаправленим у вашому BL або у вашому O / R картуванні (незалежно один від одного, навіть!).
jyoungdev

4
Приклад "ЛЮБИТЬ" лише прояснив це. ManyToMany - мій тип відображення.
Абдулла Хан

1
Супер. Це так добре пояснюється (і в контексті прикладу ОП)
Анупам

1
Не відповідає належним чином на питання. ви сумуєте за багатьма частинами. хоча один до багатьох і багато хто до одного - це питання сприйняття, ця відповідь цього не згадує.
Манзур Алахі

249

Схоже, всі відповідають One-to-manyпроти Many-to-many:

Різниця між One-to-many, Many-to-oneі Many-to-Manyце:

One-to-manyvs Many-to-one- питання перспективи . Unidirectionalvs Bidirectionalне вплине на відображення, але змінить спосіб доступу до даних.

  • В Many-to-oneна manyстороні буде тримати посилання зone стороні. Хороший приклад - «Держава має міста». У цьому випадку Stateє одна сторона і Cityє безліч сторін. У state_idтаблиці буде стовпчик cities.

У односпрямованої , Personклас матиме , List<Skill> skillsале Skillне матимеPerson person . У двонаправленому додаються обидва властивості, і це дозволяє отримати доступ до Personзаданого вміння (тобто skill.person).

  • З One-to-Manyодного боку буде наша точка відліку. Наприклад, "Користувач має адреси". В цьому випадку ми могли б мати три колонки address_1_id, address_2_idіaddress_3_id чи подивитися таблицю з унікальним обмеженням на user_idі address_id.

У односпрямованої , Userбуде Address address.Двонаправлені матимуть додатковий List<User> usersу Addressкласі.

  • В Many-to-Manyчлени кожної з сторін може містити посилання на довільне число членів іншої партії. Для цього використовується таблиця пошуку. Прикладом цього є відносини між лікарями та пацієнтами. У лікаря може бути багато пацієнтів і навпаки.

27
це має бути прийнята відповідь, більшість інших відповідей пропускають питання.
arg20

Перший приклад неправильний. Якщо у вас A Person і Person має @OneToMany з навичками, ця таблиця preson_skills матиме унікальне обмеження для skill_id. Таким чином, одна майстерність була б відображена лише для однієї людини. І ви не можете видобути s.persons, як є лишеs.person
Иван Николайчук

Насправді One-to-manyстосунки, як ви описуєте, - це Many-to-manyстосунки, оскільки вони personмають посилання на багатьох, skillsале skillне мають посилання на конкретну людину, і багато хто personsможе мати посилання однаковим skill. І ваші Many-to-oneстосунки насправді One-to-manyтому, що кожен навик має посилання лише на одного, personоскільки дитина має лише одну матір.
міксель

@mixel ваш коментар зрештою змусив мене переписати більшість моєї відповіді. Перевірте це ще раз! Спасибі
Олександр Сурафель

Ви розрізняєте «Один до багатьох» та «Багато хто до одного» за значенням сторони «Один». В обох прикладах "Користувач" є найбільш значущим об'єктом. Так що, коли це на «One» (користувачів і навичок, skillsє person_id) , то ви називаєте це один-ко-многим , але коли і це одна з «багатьох» (користувачі і адреси, usersє address_id) , то ви називаєте це багато-до-одного. Але структурно обидва випадки однакові і називаються "Один для багатьох".
міксель

38

1) Гуртки - це сутності / POJO / боби

2) deg - це абревіатура для ступеня, як у графах (кількість ребер)

PK = Первинний ключ, FK = Зовнішній ключ

Зауважте протиріччя між ступенем та назвою сторони. Багато відповідає ступеня = 1, а одному відповідає ступінь> 1.

Ілюстрація одного-до-багатьох багато-до-одного


1
Дійсно любите, як він пов'язує графік об'єкта в таблиці в обох напрямках.
Дмитро Міньковський

3
Див ботанік, це як Програміст почерк виглядає наступним чином : D
Mehraj Malik

Я бачу, що ти тут робив.
Нік

8

Погляньте на цю статтю: Картографування об'єктних відносин

Існує дві категорії об'єктних відносин, з якими потрібно враховуватись під час картографування. Перша категорія ґрунтується на кратності та включає три типи:

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

2
this occurs when the maximum of one multiplicity is one and the other is greater than onelolwut?
серг

3

Це дуже поширене питання, тому ця відповідь заснована на цій статті, яку я написав у своєму блозі.

Один до багатьох

Співвідношення таблиці «один до багатьох» виглядає так:

Один до багатьох

У системі реляційних баз даних взаємозв'язок таблиці один на багато пов'язує дві таблиці на основі Foreign Keyстовпця у дочірній частині, який посилається Primary Keyна батьківський рядок таблиці.

На діаграмі таблиці вище, post_idстовпець у post_commentтаблиці має Foreign Keyвідношення до стовпця postідентифікатора Primary Keyтаблиці:

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

@ManyToOne анотація

Найкращий спосіб зіставити співвідношення таблиці «один до багатьох» - це використання @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примітки JPA

Тільки тому, що у вас є можливість використовувати @OneToManyанотацію, це не означає, що це має бути варіантом за замовчуванням для кожного " для багатьох" співвідношення бази даних . Проблема з колекціями полягає в тому, що ми можемо ними користуватися лише тоді, коли кількість дочірніх записів досить обмежена.

Найкращий спосіб відобразити @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), які використовуються для синхронізації обох сторін двонаправленого об'єднання. Ви завжди повинні надавати ці методи кожного разу, коли працюєте з двосторонньою асоціацією, оскільки, в іншому випадку, ви ризикуєте дуже тонкими проблемами поширення держави .

Односторонньої @OneToManyасоціації слід уникати, оскільки вона менш ефективна, ніж використання @ManyToOneабо двостороння @OneToManyасоціація.

Щоб отримати докладнішу інформацію про найкращий спосіб зіставити @OneToManyстосунки з JPA та Hibernate, перегляньте цю статтю .

Один до одного

Співвідношення таблиці «один на один» виглядає так:

Один до одного

У системі реляційних баз даних взаємозв'язок таблиці один на один пов'язує дві таблиці на основі Primary Keyстовпця у дочірній, що також є Foreign Keyпосиланням Primary Keyна батьківський рядок таблиці.

Тому можна сказати, що дочірня таблиця ділиться Primary Keyз батьківською таблицею.

На діаграмі таблиці вище, idстовпець у post_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. Таким чином, вам навіть не потрібна двонаправлена ​​асоціація, оскільки ви завжди можете отримати PostDetailsоб'єкт, використовуючи Postідентифікатор сутності.

Відображення виглядає приблизно так:

[код мови = "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асоціації.

Щоб отримати докладнішу інформацію про найкращий спосіб зіставити @OneToOneстосунки з JPA та Hibernate, перегляньте цю статтю .

Багато-до-багатьох

Співвідношення таблиці «багато до багатьох» виглядає так:

Багато-до-багатьох

У реляційній системі баз даних багато-багато-багато табличних зв’язків пов'язують дві батьківські таблиці через дочірню таблицю, яка містить два Foreign Keyстовпці, що посилаються на Primary Keyстовпці двох батьківських таблиць.

На діаграмі таблиці вище, post_idстовпець у post_tagтаблиці також має Foreign Keyзв'язок із стовпцем postідентифікатора Primary Keyтаблиці:

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

І, tag_idстовпець у post_tagтаблиці має Foreign Keyвідношення до стовпця tagідентифікатора Primary Keyтаблиці:

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

Використання @ManyToManyкарти JPA

Ось як можна зіставити 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господарювання має унікальний бізнес-ключ, який позначений @NaturalIdанотацією, що стосується сплячого режиму . У цьому випадку унікальний бізнес-ключ є найкращим кандидатом на перевірку рівності .
  5. mappedByАтрибут postsасоціації в Tagпозначках сутностей , що в цьому двобічної зв'язку, то Postпідприємство має асоціацію. Це потрібно, оскільки лише одна сторона може володіти відносинами, а зміни передаються лише до бази даних саме з цієї сторони.
  6. Переважним Setє те, що використання методу Listз @ManyToManyменш ефективним.

Щоб отримати докладнішу інформацію про найкращий спосіб зіставити @ManyToManyстосунки з JPA та 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 (таким чином, я припускаю, і у сплячому режимі) має кумедне відповідність із відображенням графіків БД та об'єктів. Наприклад, відносини один на один часто реалізуються як відносини «багато в одному».

По-друге, перш ніж ми зможемо сказати вам, як ви повинні написати свою O / R карту, ми також повинні побачити ваш БД. Зокрема, чи може мати одне вміння кілька людей? Якщо так, у вас є стосунки «багато до багатьох»; інакше багато-на-один.

По-третє, я вважаю за краще не реалізовувати відносини "багато на багато" безпосередньо, а замість цього моделювати "таблицю приєднання" у вашій моделі домену, тобто трактувати її як сутність, як це:

class PersonSkill 
{
    Person person;
    Skill skill;    
}

Тоді ви бачите, що у вас є? У вас два стосунки один на багато. (У цьому випадку у Особи може бути колекція PersonSkills, але не буде колекції Навичок.) Однак деякі вважають за краще використовувати відносини «багато-багато-багато» (між Особою та Майстерністю); це суперечливо.

По-четверте, якщо у вас є двосторонні стосунки (наприклад, у особи є не лише колекція вмінь, але й у майстерності є колекція осіб), NHibernate не застосовує для вас двонаправленість; воно розуміє лише двонаправленість відносин на постійність.

По-п'яте, багато в одному набагато простіше правильно користуватися в NHibernate (і я вважаю, що перебуває в сплячому режимі), ніж один-до-багатьох (картографування колекції).

Удачі!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.