Лучший подход для связывания различных типов объектов в JPA

Краткая версия для поспешности:

В моей модели домена существуют разные таблицы/сущности, которые имеют одно и то же поле (UUID). Существует таблица, где мне нужно связать строки/экземпляры таких объектов с другими объектами, управляемыми JPA. Другими словами, экземпляр поля в этой таблице ссылок не будет известен заранее. Два подхода, о которых я могу думать, следующие:

  • Используйте абстрактный объект и стратегию TABLE_PER_CLASS, или
  • используйте @MappedSuperClass, чтобы сохранить имя класса экземпляра в таблице ссылок, или что-то подобное, что позволяет мне определять логику для получения фактического экземпляра из правой таблицы.

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

Длинная версия, если вы хотите больше фона:

У меня есть модель базы данных/объекта, в которой у многих типов есть общее поле: универсальный уникальный идентификатор (UUID). Причина этого в том, что экземпляры этих типов могут быть изменены. Изменения следуют за моделью команды, и их данные могут быть инкапсулированы и сами сохраняются. Позвольте называть такое изменение "мутацией". Должно быть возможно выяснить, какие мутации существуют в базе данных для любого данного объекта, и наоборот, на каком объекте действует сохраненная мутация.

Возьмите следующие объекты с UUID в качестве (чрезвычайно упрощенного) примера: изменяемые объекты

Чтобы сохранить "мутации", мы используем таблицу/объект, называемый MutationHolder. Чтобы связать мутацию с ее целевой сущностью, там MutationEntityLink. Единственная причина, по которой эти данные не относятся непосредственно к MutationHolder, состоит в том, что могут быть прямые или косвенные ссылки, но это мало важно здесь, поэтому я ее не заметил: мутационные сущности

Вопрос сводится к тому, как я могу моделировать поле entity в MutationEntityLink. Есть два подхода, о которых я могу думать.

Первый заключается в создании абстрактного @Entity аннотированного класса с полем UUID. Customer, Contract и Address расширят его. Таким образом, это стратегия TABLE_PER_CLASS. Я предполагаю, что я мог бы использовать это как тип для поля entity, хотя я не уверен. Однако я опасаюсь, что это может привести к серьезному снижению производительности, поскольку JPA необходимо будет запросить многие таблицы, чтобы найти фактический экземпляр.

Во-вторых, просто используйте @MappedSuperClass и просто сохраните UUID для объекта в поле entity MutationEntityLink. Чтобы получить фактическую сущность с этим UUID, мне пришлось бы ее решать программно. Добавление дополнительного столбца с именем класса объекта или другим, что позволяет мне идентифицировать его или вставить его в запрос JPQL. Это требует большей работы, но кажется более эффективным. Я не прочь кодировать некоторые классы утилиты или делать некоторые рефлексии/пользовательскую работу аннотации, если это необходимо.

Мой вопрос: какой из этих подходов кажется лучшим? Кроме того, у вас может быть лучшее предложение или заметить, что я что-то упускаю; например, может быть, есть способ добавить столбец типа, даже с наследованием TABLE_PER_CLASS, чтобы указать JPA на правую таблицу? Возможно, вы пробовали что-то подобное и хотите предупредить меня о многочисленных проблемах, которые возникнут.

Дополнительная информация:

  • Мы создаем схему базы данных, поэтому мы можем добавлять все, что захотим.
  • Одна стратегия наследования таблицы не является вариантом. Таблицы должны оставаться различными. По той же причине объединенное наследование также не подходит.
  • Поставщик JPA - это Hibernate, и использование вещей, которые не являются частью стандарта JPA, не является проблемой.

Ответ 1

Если сущности не имеют ничего общего, кроме того, что у меня есть uuid, я бы использовал второй подход, который вы описываете: используйте MappedSuperclass. При создании общего суперкласса сущность не позволит вам использовать другую стратегию наследования, если потребуется, потребуется таблица для этого супер-объекта, даже если экземпляры не существуют, и с бизнес-точки зрения это просто неправильно.

Сама связь может быть реализована несколькими способами, например. вы можете подклассифицировать MutationEntityLink для каждого объекта для сопоставления (например, CustomerMutationEntityLink и т.д.) или делать то, что вы описали, т.е. хранить только uuid, а также некоторую информацию о дискриминаторе/типе и решать программно (мы используем этот подход для что-то подобное кстати.).

Ответ 2

Вам нужно использовать @MappedSuperclass при наследовании ассоциаций/методов/свойств, тогда как TABLE_PER_CLASS обычно используется, когда у вас есть сущность и суб-сущности. Если существуют объекты, имеющие связь с базовым классом в модели, то используйте TABLE_PER_CLASS, поскольку базовый класс ведет себя как сущность. В противном случае, поскольку базовый класс будет включать свойства/атрибуты и методы, которые являются общими для таких объектов, не связанных друг с другом, использование @MappedSuperclass было бы лучшей идеей

Пример1: Вам нужно установить аварийные сигналы для некоторых различных видов деятельности, таких как "принимать лекарства", "позвонить маме", "пойти к врачу" и т.д. Содержимое сообщения о тревоге не имеет значения, вы потребуется напоминание. Поэтому используйте TABLE_PER_CLASS, поскольку сообщение о тревоге, которое является вашим базовым классом, похоже на объект здесь.

Пример2:. Предположим, что базовый класс AbstractDomainObject позволяет вам создать идентификатор входа, loginName, дату создания/изменения для каждого объекта, где ни один объект не имеет ассоциации с базовым классом, вам нужно будет указать ассоциация ради очистки позже, например, "Компания", "Университет" и т.д. В этой ситуации лучше использовать @MappedSuperclass.