В чем разница между однонаправленными и двунаправленными ассоциациями JPA и Hibernate?

135

В чем разница между однонаправленными и двунаправленными ассоциациями?

Поскольку таблица, сгенерированная в БД, одинакова, единственное различие, которое я обнаружил, состоит в том, что каждая сторона двунаправленных ассоциаций будет иметь ссылку на другую, а однонаправленная - нет.

Это однонаправленная ассоциация

public class User {
    private int     id;
    private String  name;
    @ManyToOne
    @JoinColumn(
            name = "groupId")
    private Group   group;
}

public class Group {
    private int     id;
    private String  name;
}

Двунаправленная ассоциация

public class User {
    private int     id;
    private String  name;
    @ManyToOne
    @JoinColumn(
            name = "groupId")
    private Group   group;
}
public class Group {
    private int         id;
    private String      name;
    @OneToMany(mappedBy="group")
    private List<User>  users;
}

Разница в том, содержит ли группа ссылку на пользователя.

Интересно, это единственная разница? что рекомендуется?

hguser
источник
7
Теперь группа будет знать, каких пользователей она содержит. Я не думаю, что это в любом случае небольшая разница.
Сатадру Бисвас
5
Двунаправленные отношения стали хаосом для меня, когда дело доходит до обновления. :)
diyoda_

Ответы:

152

Основным отличием является то, что двунаправленные отношения обеспечивают навигационный доступ в обоих направлениях, так что вы можете получить доступ к другой стороне без явных запросов. Также это позволяет применять каскадные параметры в обоих направлениях.

Обратите внимание, что навигационный доступ не всегда хорош, особенно для отношений «один к очень многим» и «многие к очень многим». Представьте себе, Groupчто содержит тысячи Userс:

  • Как бы вы получили к ним доступ? При таком количестве Users обычно вам нужно применить некоторую фильтрацию и / или нумерацию страниц, чтобы вам все равно нужно было выполнить запрос (если только вы не используете фильтрацию коллекции , что для меня выглядит хаком). Некоторые разработчики могут применять фильтрацию в памяти в таких случаях, что явно не сказывается на производительности. Обратите внимание, что наличие таких отношений может побудить разработчиков такого рода использовать их без учета последствий для производительности.

  • Как бы вы добавили новые Userс Group? К счастью, Hibernate при сохранении отношения смотрит на собственную сторону отношений, поэтому вы можете только установить User.group. Однако, если вы хотите, чтобы объекты в памяти были единообразными, вам также необходимо добавить Userв Group.users. Но это заставит Hibernate извлечь все элементы Group.usersиз базы данных!

Так что я не могу согласиться с рекомендацией из Best Practices . Вам необходимо тщательно спроектировать двунаправленные отношения, учитывая варианты использования (нужен ли вам навигационный доступ в обоих направлениях?) И возможные последствия для производительности.

Смотрите также:

axtavt
источник
Привет, спасибо. Похоже, вы являетесь специалистом по спящему режиму, поскольку ответили на мой вопрос: stackoverflow.com/questions/5350770/… . И теперь я не уверен, что отношения сохраняются, так как я не могу больше писать в комментарии, поэтому выкладываю их здесь dpaste.de/J85m. Пожалуйста, проверьте, если это возможно. :)
hguser
@hguser: Если вы , наконец , решили сделать вас отношения двунаправленным, я думаю , было бы лучше , чтобы вызов setGroup()от addUser()того , чтобы держать обе стороны последовательны.
axtavt
как насчет того, чтобы я не вызывал setGroup () внутри group.addUser ()?
hguser
@hguser: отношения не будут сохраняться, см. пункт 2 в ответе. Вы можете звонить setGroup()без addUser(), но это приведет к несогласованному состоянию объектов в памяти.
Axtavt
Я обнаружил, что не могу правильно составить карту, можете ли вы потратить некоторое время, чтобы проверить мой проект на github? это маленький проект.
hguser
31

Есть два основных различия.

Доступ к сторонам ассоциации

Первый связан с тем, как вы получите доступ к отношениям. Для однонаправленной ассоциации вы можете перемещаться по ассоциации только с одного конца.

Таким образом, для однонаправленной @ManyToOneассоциации это означает, что вы можете получить доступ к отношениям только со стороны ребенка, где находится внешний ключ.

Если у вас есть однонаправленная @OneToManyсвязь, это означает, что вы можете получить доступ только к той родительской стороне, где находится внешний ключ.

Для двунаправленной @OneToManyассоциации вы можете перемещаться по ассоциации обоими способами, либо с родительской, либо с дочерней стороны.

Вам также необходимо использовать служебные методы добавления / удаления для двунаправленных ассоциаций, чтобы обеспечить правильную синхронизацию обеих сторон .

Производительность

Второй аспект связан с производительностью.

  1. Для @OneToMany, однонаправленные объединения не выполняют, а также двунаправленных из них .
  2. Для @OneToOne, двунаправленная ассоциация будет вызывать родительскую быть неправдоподобной жадностью , если Hibernate не может сказать , является ли прокси должен быть назначен или нулевое значением .
  3. Для @ManyToMany, тип коллекции имеет большое значение, так как Setsработают лучше, чемLists .
Влад Михалча
источник
11

С точки зрения кодирования, двунаправленное отношение является более сложным для реализации, потому что приложение отвечает за синхронизацию обеих сторон согласно спецификации 5 JPA (на странице 42). К сожалению, приведенный в спецификации пример не дает более подробной информации, поэтому он не дает представления об уровне сложности.

Когда не используется кэш второго уровня, обычно нет проблем с неправильной реализацией методов отношений, поскольку экземпляры отбрасываются в конце транзакции.

При использовании кэша второго уровня, если что-либо повреждено из-за неправильно реализованных методов обработки отношений, это означает, что другие транзакции также будут видеть поврежденные элементы (кэш второго уровня является глобальным).

Правильно реализованные двунаправленные отношения могут упростить запросы и код, но их не следует использовать, если они не имеют смысла с точки зрения бизнес-логики.

Константино Кронембергер
источник
9

Я не уверен на 100%, что это единственная разница, но это главное отличие. Также рекомендуется иметь двунаправленные ассоциации в документах Hibernate:

http://docs.jboss.org/hibernate/core/3.3/reference/en/html/best-practices.html

В частности:

Предпочитают двунаправленные ассоциации: однонаправленные ассоциации сложнее запрашивать. В большом приложении почти все ассоциации должны быть ориентированы в обоих направлениях в запросах.

У меня лично есть небольшая проблема с этой общей рекомендацией - мне кажется, есть случаи, когда у ребенка нет практической причины знать о своем родителе (например, почему элемент заказа должен знать о заказе, в котором он находится) связано с?), но я вижу ценность в этом разумную часть времени, а также. И поскольку двунаправленность на самом деле ничего не ранит, я не считаю это слишком нежелательным для соблюдения.

kvista
источник
Двунаправленность может повредить в некоторых случаях, как объяснил Акставт! Я был бы очень осторожен с каждым отношением, отображенным в Hibernate! У вас может быть бесконечное время, необходимое для загрузки вещей в Hibernate, если вы точно не знаете, что делаете. Тщательно продумайте варианты использования и используйте разные классы моделей для разных вариантов использования. F.ex. в списке вещей мне не нужны все связанные объекты, только метка и идентификатор. Таким образом, для меня сущность списка отличается (и очень проста) от сущности детализации.
cslotty