Можно ли удалить столбец дискриминатора в наследовании одной таблицы Hibernate? - PullRequest
5 голосов
/ 02 января 2012

Мы используем одиночное наследование таблиц для каждой таблицы в нашем приложении. Это позволяет различным экземплярам одного и того же стека приложений работать с одними и теми же DAO, в то время как их объекты могут немного отличаться, потенциально содержа информацию, уникальную для этого экземпляра. Абстрактный класс определяет базовую структуру таблицы, а расширение определяет дополнительные столбцы, если это необходимо для этого экземпляра:

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@Table(name = "client")
public abstract class Client extends AbstractPersistable<Long> {
    // ...
}

приложение A:

@Entity
public class ClientSimple extends Client {
    private String name;
    // getter, setter
}

приложение B:

@Entity
public class ClientAdvanced extends Client {
    private String description;
    // getter, setter
}

Теперь DAO может работать с Client объектами для приложений A и B, но приложение B может определять дополнительную информацию для своего клиентского объекта, которая может быть прочитана методом менеджера, уникальным для приложения B:

приложение A:

Client client = new ClientSimple();
clientDao.save(client);

приложение B:

Client client = new ClientAdvanced();
clientDao.save(client);

К сожалению, это означает, что в каждой таблице есть столбец DTYPE (или любое другое имя, которое я могу выбрать). Есть ли способ избавиться от этого? Нам это не нужно, и оно использует пространство БД ...

Спасибо!


EDIT

Важно отметить: @MappedSuperclass не будет работать. Мы используем QueryDSL в качестве уровня абстракции HQL. Это требует автоматически сгенерированных классов Query Type для запросов сохранения типа. Однако они будут сгенерированы правильно, только если абстрактный класс помечен @Entity.

Это не обязательно, потому что мы хотим выполнить запрос к абстрактному классу Client, в то время как в действительности запрашиваем ClientSimple в приложении A и ClientAdvanced в приложении B:

Так что в любом приложении это будет работать:

query.where(QClient.client.name.equals("something");

и в приложении B это будет работать:

query.where(QClientSimple.client.description.equals("something else");

EDIT2 - выкинуть

Кажется, что все сводится к следующему: можно ли настроить hibernate во время развертывания, чтобы установить тип дискриминатора для унаследованной сущности на фиксированное значение. Итак, в моем примере Client всегда будет ClientSimple в одном приложении и ClientAdvanced в другом, чтобы мне не приходилось хранить эту информацию в базе данных?

Как я уже сказал: каждое приложение будет экземпляром стека базовых приложений. Каждое приложение может определять дополнительные столбцы для своей локальной базы данных, но ВСЕ объекты будут одного типа для этого экземпляра, поэтому мы гарантируем, что дискриминатор всегда одинаков, что делает его избыточным в базе данных и сценарии использования для конфигурации гибернации.

Ответы [ 3 ]

7 голосов
/ 24 января 2014

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

Это можно сделать с помощью аннотации @ DiscriminatorFormula в Hibernate. Следующее описание основано на книге Java Persistence with Hibernate , раздел 5.1.3; соответствующая часть начинается на странице последнего абзаца на странице 202.

С помощью @ DiscriminatorFormula вы можете предоставить оператор SQL, который определяет значение discriminator при извлечении соответствующих строк из базы данных. В вашем случае это должна быть простая строка, которая оценивает какое-то произвольно выбранное значение. Чтобы это работало, вам нужно выбрать имя, которое будет использоваться для вашей Client сущности. Предположим, что вы выбрали «GenericClient» в качестве имени entity. Это имя должно появляться в аннотации @Entity в качестве значения атрибута name. Итак, полный пример в вашем случае будет выглядеть следующим образом.

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@Table(name = "client")
@DiscriminatorFormula("'GenericClient'")  // *1*
public abstract class Client extends AbstractPersistable<Long> {
    // ...
}

// Application A
@Entity
@DiscriminatorValue("GenericClient")  // *2*
public class SimpleClient extends Client {
    // ...
}


// Application B
@Entity
@DiscriminatorValue("GenericClient")  // *3*
public class AdvancedClient extends Client {
    // ...
}

Строка, обозначаемая ' 1 ', является частью фрагмента SQL, который всегда возвращает GenericClient в качестве значения. subclasses из Client всегда должен быть аннотирован @DiscriminatorValue("GenericClient"). Это означает, что когда Hibernate выбирает строки из БД, тип создаваемого объекта всегда будет определенным подклассом Client.

Если пакет, в котором находятся подклассы Client, и имя подклассов являются фиксированными :

В этом случае @DiscriminatorValue("GenericClient") для подклассов не потребуется, все, что вам нужно сделать, это:

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@Table(name = "client")
@DiscriminatorFormula("'com.example.fixed.path.FixedSubClassName'")
public abstract class Client extends AbstractPersistable<Long> {
    // ...
}

Подклассам не потребуются аннотации. discriminator-value по умолчанию равно entity-name, которое само по умолчанию соответствует полному имени класса.

Примечание: Оператор SQL внутри @DiscriminatorFormula() может быть любым действительным оператором SQL для целевого сервера БД.

1 голос
/ 03 января 2012

В Hibernate для иерархии «Одна таблица на класс» всегда требуется столбец дискриминатора, чтобы различать объекты, поскольку все классы в одной иерархии хранятся в одной таблице. Вот пример Hibernate Single Table для Иерархии Классов .

Но вы можете рассмотреть другую схему Иерархии, как показано ниже:

Hibernate Single Table на подкласс

Преимущества

  • Использование этой иерархии не требует сложных изменений схема базы данных при изменении одного родительского класса.
  • Хорошо работает с мелкой иерархией.

Недостатки

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

Спящий стол Hibernate на класс бетона

Преимущества

  • Это самый простой способ реализации карт наследования.

Недостатки

  • Данные, принадлежащие родительскому классу, разбросаны по ряду таблицы подклассов, представляющие конкретные классы.
  • Эта иерархия не рекомендуется для большинства случаев.
  • Изменения в родительском классе отражаются в большом количестве таблиц
  • Запрос, составленный в терминах родительского класса, может вызвать большой количество операций выбора

Я бы посоветовал вам взглянуть на схему Single Table Per Subclass. Хотя я не уверен в ваших точных требованиях. Но это может помочь.

1 голос
/ 02 января 2012

Если вам никогда не нужно использовать и ClientSimple, и ClientAdvanced в одном приложении, вы можете объявить Client как @MappedSuperclass вместо @Entity.

...