4.2.3.2. Ассоциация Many-to-Many через связующую сущность

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

Продемонстрируем этот подход на примере сущностей Airport и DutyFree. В одном аэропорту может располагаться множество разных сетей магазинов беспошлинной торговли, и одна сеть duty-free может быть представлена во множестве разных аэропортов. Предположим, что кроме связи сущностей мы хотим хранить ещё и валюту, используемую в данном магазине в данном аэропорту:

association recipe 2
  • Airport.java - сущность Airport содержит one-to-many коллекцию экземпляров AirportDutyFree.

    В редакторе сущностей Studio установите следующие свойства для атрибута dutyFreeShops: Attribute type - COMPOSITION, Cardinality - ONE_TO_MANY.

    @Composition
    @OnDelete(DeletePolicy.CASCADE)
    @OneToMany(mappedBy = "airport")
    protected List<AirportDutyFree> dutyFreeShops;
  • DutyFree.java - сущность DutyFree также содержит one-to-many коллекцию экземпляров AirportDutyFree.

    В редакторе сущностей Studio установите следующие свойства для атрибута airports: Attribute type - COMPOSITION, Cardinality - ONE_TO_MANY.

    @Composition
    @OnDelete(DeletePolicy.CASCADE)
    @OneToMany(mappedBy = "dutyFree")
    protected List<AirportDutyFree> airports;
  • AirportDutyFree.java - таким образом, связующая сущность AirportDutyFree содержит два ссылочных атрибута с отношением many-to-one: airport и dutyFree:

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "AIRPORT_ID")
    protected Airport airport;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "DUTY_FREE_ID")
    protected DutyFree dutyFree;
  • views.xml - представление airport-duty-free экрана редактирования аэропорта содержит атрибут-коллекцию dutyFreeShops (со ссылками на AirportDutyFree), а в ней атрибуты dutyFree и currency.

    Представление dutyFree-airport следует той же логике: оно содержит атрибут-коллекцию airports (со ссылками на AirportDutyFree), а в ней атрибуты airport и currency.

  • duty-free-edit.xml - XML-дескриптор экрана редактирования магазина duty-free определяет источник данных для экземпляра DutyFree и вложенный источник для его аэропортов. Кроме того, экран содержит таблицу, отображающую аэропорты, и действие, позволяющее выбирать аэропорт напрямую, минуя экран редактирования связующей сущности AirportDutyFree.

В результате редактирование экземпляра DutyFree работает следующим образом:

В экране редактирования DutyFree отображается таблица аэропортов и выпадающий список валют.

По нажатию Add airport открывается экран выбора Airport, и пользователь может как выбрать аэропорт, так и открыть экран его редактирования. Когда экземпляр аэропорта выбран, создаётся новый экземпляр сущности AirportDutyFree, которому проставляется валюта по умолчанию. Этот экземпляр связующей сущности не сохраняется в базу данных, а добавляется в источник данных airportsDs экрана редактирования DutyFree.

Когда пользователь нажимает OK в экране редактирования аэропорта, изменённый экземпляр аэропорта сохраняется и в базу данных, и в источник данных airportsDs экрана редактирования DutyFree, так как сущность Airport является полностью независимой.

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

Когда пользователь нажимает OK в экране редактирования магазина, изменённый экземпляр DutyFree вместе со всеми измененными экземплярами AirportDutyFree отправляется на middleware в метод DataManager.commit() и сохраняется в базе данных в рамках одной транзакции.