Сопоставить базу данных column1, column2, columnN с набором элементов - PullRequest
2 голосов
/ 16 июля 2009

В устаревших таблицах базы данных у нас есть пронумерованные столбцы, такие как C1, C2, C3, C100 или M1, M2, M3, M100.
Эти столбцы представляют данные BLOB.

Невозможно что-либо изменить в этой базе данных.

Используя JPA Embeddable, мы отображаем все столбцы в отдельные поля. И затем во время встраивания мы переопределяем имена, используя 100 аннотаций переопределения.

Недавно мы перешли на Hibernate, и я нашел такие вещи, как UserCollectionType и CompositeUserType . Но я не нашел ни одного варианта использования, близкого к моему.

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

Edit:
Как вы, наверное, заметили, имена столбцов могут отличаться от таблицы к таблице. Я хочу создать один тип, такой как "LegacyArray", без необходимости указывать все @Columns каждый раз, когда я использую этот тип. Но вместо этого я бы использовал

  @Type(type = "LegacyArrayUserType",
        parameters =
   {
      @Parameter(name = "prefix", value = "A"),
      @Parameter(name = "size", value = "128")
   })
   List<Integer> legacyA;

  @Type(type = "LegacyArrayUserType",
        parameters =
   {
      @Parameter(name = "prefix", value = "B"),
      @Parameter(name = "size", value = "64")
   })
   List<Integer> legacyB;

Ответы [ 6 ]

3 голосов
/ 21 июля 2009

Я могу придумать пару способов сделать это.

1. Создайте представления для информации о коллекции, которая имитирует нормализованную структуру таблицы, и сопоставьте ее с Hibernate как коллекцию:

Предполагая, что ваша существующая таблица называется primaryentity, я бы создал представление, похожее на следующее:

-- untested SQL...
create view childentity as
(select primaryentity_id, c1 from primaryentity union
select primaryentity_id, c2 from primaryentity union
select primaryentity_id, c3 from primaryentity union
--...
select primaryentity_id, c100 from primaryentity)

Теперь, с точки зрения Hibernate, childentity - это просто нормализованная таблица с внешним ключом, равным primarykey. Отображение этого должно быть довольно простым и рассматривается здесь:

Преимущества этого подхода:

  • С точки зрения Hibernate, таблицы нормализованы, это довольно простое отображение
  • Нет обновлений в существующих таблицах

Недостатки:

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

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


2. Используйте средство динамического отображения модели Hibernate , чтобы сопоставить ваши свойства C1, C2, C3 с Map , и получить некоторый код, который ваш слой DAO делает в соответствующем диалоге между свойство Карта и Коллекция:

Я никогда не делал этого сам, но я верю, что Hibernate позволяет вам отображать таблицы в HashMaps. Я не уверен, насколько динамически Hibernate позволяет вам это делать (т. Е. Можете ли вы просто указать имя таблицы и иметь Hibernate, автоматически отображающий все столбцы?), Но я думаю, что это еще один способ.

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

Преимущества этого подхода:

  • Нет изменений в базе данных вообще
  • Данные обновляются
  • Отображение O / R относительно просто

Недостатки:

  • Много слесарного дела в слое DAO для отображения соответствующих типов
  • Используются экспериментальные функции Hibernate, которые могут измениться в будущем
1 голос
/ 22 июля 2009

[РЕДАКТИРОВАТЬ] Я предлагаю использовать CompositeUserType. Вот пример . На странице 228f также есть хороший пример в книге «Сохранение Java в спящем режиме».

Это позволяет вам обрабатывать множество столбцов как один объект в Java.

Отображение выглядит так:

@org.hibernate.annotations.Columns(columns = {
    @Column(name="C1"),
    @Column(name="C2"),
    @Column(name="C3"),
    ...
})
private List<Integer> c;

Hibernate загрузит все столбцы одновременно во время обычного запроса.

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

for (int i=1; i<numColumns; i++)
    if (i < list.size())
        resultSet.setInt(index+i, list.get(i));
    else
        resultSet.setNull(index+i, Hibernate.INTEGER.sqlType());

В nullSafeGet необходимо создать список и прекратить добавление элементов, когда столбец равен NULL. Для дополнительной безопасности я предлагаю создать собственную реализацию списка, которая не позволяет выходить за пределы количества столбцов (наследовать от ArrayList и переопределять ensureCapacity()).

[EDIT2] Если вы не хотите вводить все аннотации @Column, используйте для них генератор кода. Это может быть так же просто, как скрипт, которому вы даете имя и номер, и он печатает @Column (...) в System.out. После запуска сценария просто скопируйте и вставьте данные в источник.

Единственным другим решением было бы получить доступ к внутреннему API Hibernate для создания этой информации во время выполнения, но этот API является внутренним, поэтому многие вещи являются частными. Вы можете использовать отражение Java и setAccessible(true), но этот код, вероятно, не выдержит следующего обновления Hibernate.

1 голос
/ 20 июля 2009

Извините, если я неправильно понимаю вашу проблему здесь, я не знаю много о Hibernate. Но не могли бы вы просто объединить во время выбора из базы данных, чтобы получить что-то вроде того, что вы хотите?

Как:

SELECT whatever
     , C1||C2||C3||C4||...||C100 AS CDATA
     , M1||M2||M3||M4||...||M100 AS MDATA
FROM ...
WHERE ...

(Конечно, оператор конкатенации отличается в разных СУБД.)

1 голос
/ 16 июля 2009

Лично я думаю, что дизайн звучит так, как будто он ломается первая нормальная форма для реляционных баз данных. Что произойдет, если вам нужен C101 или M101? Изменить свою схему снова? Я думаю, что это очень навязчиво.

Если вы добавите Hibernate в смесь, это будет еще хуже. Добавление C101 или M101 означает необходимость изменения ваших объектов Java, ваших отображений Hibernate, всего.

Если у вас есть отношения 1: m с таблицами C и M, вы сможете обрабатывать случаи, которые я только что привел, добавляя дополнительные строки. Ваши объекты Java содержат Collection или Collection . Ваши отображения в Hibernate один-ко-многим не меняются.

Возможно, причина в том, что вы не видите примеров Hibernate, подходящих для вашего случая, потому что этот дизайн не рекомендуется.

Если вам нужно, может быть, вам стоит взглянуть на Отображение компонентов Hibernate .

ОБНОВЛЕНИЕ: должным образом отмечается тот факт, что это наследие. Моя точка зрения при обсуждении первой нормальной формы - это как для тех, кто может найти этот вопрос в будущем, так и для человека, который разместил вопрос. Я не хотел бы отвечать на вопрос таким образом, чтобы он молча утверждал этот проект как «хороший».

Уместно указать отображение компонента Hibernate, потому что знание имени того, что вы ищете, может быть ключевым при поиске. Hibernate позволяет объектной модели быть более детализированной, чем реляционная модель, которую она отображает. Вы можете смоделировать денормализованную схему (например, объекты Name и Address как часть более крупного объекта Person). Это просто название, которое они дают такую ​​технику. Это может помочь найти и другие примеры.

0 голосов
/ 24 июля 2009

Я тоже никогда не пользовался Hibernate.

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

0 голосов
/ 16 июля 2009

Вы можете использовать UserType s для сопоставления заданного количества столбцов с любым типом, который вы пожелаете. Это может быть коллекция, если (например) коллекции всегда ограничены по размеру известным числом элементов.

Прошло много времени (> 3 года) с тех пор, как я использовал Hibernate, поэтому я довольно ржавый, но я помню, что это было очень легко сделать; ваш BespokeUserType класс получает от ResultSet до гидрат ваш объект от него.

...