Как обрабатывать двунаправленные отношения при построении спящих объектов? - PullRequest
4 голосов
/ 06 июля 2011

Я хочу смоделировать отношения между двумя объектами, группой и учетной записью в JPA / Hibernate.Учетная запись может иметь несколько групп, но не наоборот, поэтому у нас есть связь OneToMany между учетной записью и группой.Мой рабочий коллега предложил смоделировать сущности Account и Group, например

public class Account {
    private List<Group> groups = new ArrayList<Group>();

    public Account() {}

    public void setGroups(List<Group> usergroups) {
        this.groups = groups;
    }

    @OneToMany(mappedBy = "account")
    public List<Group> getGroups() {
        return groups;
    }
}

и

public class Group {
    private String name;
    private Account account;

    public Group() {}

    public Group(String name, Account account) {
        this.name = name;
        addToAccount(account);
    }

    public void addToAccount(Account account) {
        setAccount(account);
        List<Group> accountGroups = account.getGroups();
        accountGroups.add(this);
    }

    @ManyToOne
    public Account getAccount() {
        return account;
    }

    public void setAccount(Account account) {
        this.account = account;
    }
}

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

Однако я считаю, что вызов метода addToAccountв конструкторе не очень хорошая идея, потому что

  1. List из Group s лениво выбирается, поэтому вызов метода addToAccount требует открытой транзакции.Таким образом, конструктор Group может быть вызван только внутри открытой транзакции.На мой взгляд, это очень раздражающее ограничение.

  2. Объект Account, указанный в качестве аргумента для конструктора Group, изменен конструктором.На мой взгляд, это удивительный побочный эффект конструктора Group, который не должен происходить.

Мое предложение заключалось в том, чтобы лучше использовать простой конструктор, такой как

 public Group(String name, Account account) {
            this.name = name;
            this.account = account;
        }

и справиться с двунаправленной связью вручную.Но, возможно, я ошибаюсь.Есть ли общий способ обработки двунаправленных отношений при построении спящих сущностей?

Ответы [ 2 ]

3 голосов
/ 06 июля 2011

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

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

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

В-третьих, если вы избавляетесь от метода addToAccount и используете доступ к полю, вы можете сделать ваши классы неизменяемыми, и это хорошо.

0 голосов
/ 06 июля 2011

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

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

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

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

Короче->

public void Group.setAccounts(Account a)
{
  this.account = a;
}

фактически эквивалентно тому, что вы делаете выше. База данных запросит и заполнит список чем-то похожим на:

//Pseudo SQL
    SELECT g.id FROM Group g WHERE g.account_id = :account_id

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

(Не усложняйте, это выглядит просто. Надеюсь, длинное объяснение даст вам представление о том, что происходит в JPA)

...