среда, 31 марта 2010 г.

Hibernate: это должен помнить каждый - 2


При определении идентификатора - первичного ключа - таблицы в Hibernate можно явно указать стратегию генерации его значения. Делается это в мэпинге с помощью тега generator, у которого указывается атрибут class. Например, так:

<id name="uid" column="uuid" type="string" length="32">

   <generator class="ru.naumen.bpm.commons.util.PrefixUUIDGenerator"/>

</id>


Помимо того, что можно определить свою стратегию генерации как класс, реализующий интерфейс org.hibernate.id.IdentifierGenerator, фреймворк содержит ряд уже готовых генераторов. Рассмотрим их подробнее.



increment - генерирует идентификаторы типов long, int и short, которые являются уникальными только если никакой другой процесс не добавляет данные в ту же самую таблицу. Данную стратегию нельзя использовать в кластерном окружении.

identity - поддерживает identity-столбцы в DB2, MySQL, MS SQL Server, Sybase и HypersonicSQL. Возвращаемый идентификатор имеет тип long, int или short.

sequence - использует последовательности в DB2, PostgreSQL, Oracle, SAP DB, McKoi или генераторы в Interbase. Возвращаемый идентификатор имеет тип long, int или short.

hilo - использует hi/lo алгоритм для рационального генерирования идентификаторов типа long, int или short уникальных для таблицы или колонки (по умолчанию - hibernate_unique_key и next_hi, соответственно). Hi/lo алгоритм генерирует идентификаторы, которые уникальны только для конкретной базы данных. Не стоит использовать данную стратегию для соединений, установленных с помощью JTA или с помощью определяемых пользователем соединений.

seqhilo - использует hi/lo алгоритм для рационального генерирования идентификаторов типа long, int или short. В качестве источника данных используются именованные последовательности.

uuid - использует 128-битный UUID-алгоритм для генерации идентификаторов типа String, уникальных в пределах сети (используется IP-адрес). UUID кодируется как строка шестнадцатеричных цифр длиной 32-символа.

guid - использует генерируемую СУБД GUID-строку на MS SQL Server или MySQL.

native - выбирает идентификатор, последовательность или hilo в зависимости от возможностей используемой СУБД.

assigned - позволяет приложению самостоятельно устанавливать идентификатор объекта перед тем как вызвать save(). Данная стратегия установлена по-умолчанию и будет использоваться, если не указывать тег generator вообще.

select - извлекает первичный ключ, назначенный триггером. Триггер выбирает некоторое уникальное значение строки и возвращает значение первичного ключа.

foreign - использует идентификатор другого связанного объекта. Обычно используется в связке с <one-to-one> ассоциацией.

Оригинал статьи: Understanding Hibernate <generator> element.

Понравилось сообщение - подпишитесь на блог или читайте меня в twitter

16 комментариев:

hazzik комментирует...

Для возможностей проведения недо-интеграционных тестов с подменной базой sqlite у нас в мапингах используются генераторы native.
Это, также, очень удобно при миграции базы с одной платформы на другую.

Pavel Samolisov комментирует...

А у нас написаны свои генераторы, которые создают id вида corebo203030020...0 длиной 32 символа. corebo - 6-ти буквенный префикс, который уникален для каждого класса бизнес-сущностей. Очень удобно - по идентификатору сразу понятно какой класс подгружать.

Mikhail Khomutetskiy комментирует...

2 Pavel Samolisov:
Недостаток таких UUID в том, что индекс СУБД может не поддерживать индексацию всех 32 символов, а т.к. префикс идет перед самим идентификатором, то и поиск в БД по такому идентификатору может быть медленнее. Да даже если поддерживаются все 32 символа в индексе, то первые 6 символов почти всегда будут одними и теми же, что плохо для индекса. Для выхода из этой ситуации можно, например, создавать обратный индекс(если СУБД позволяет) или вместо префикса использовать постфикс.

Pavel Samolisov комментирует...

Это уже детали реализации на самом деле. Мне понравилась сама идея хранить в uuid-е информацию о классе, к которому принадлежит сущность с таким uuid-ом.

Анонимный комментирует...

Здравствуйте!

Всегда было интересно почему одни используют Hibernate, а другие OpenJPA, например, и т.п.
А почему лично вы используете Hibernate, а не, скажем, EclipseLink ? Интересно, т.к. выбор обычно основан обычно на опыте.

Pavel Samolisov комментирует...

Почему я лично или почему компания Naumen? Если про компанию - то вероятно потому что разработчики больше знакомы с Hibernate. А разработчики больше знакомы с Hibernate потому что его чаще используют и он чаще присутствует в вакансиях. Замкнутый круг какой-то

Анонимный комментирует...

Отлично!

Тогда я буду использовать EclipseLink. TopLink я уже использовал. Теперь надо на JPA 2 перебираться - книгу уже читаю, некоторые полезные вещи из Hibernate пришли и какой-никакой стандарт, вроде.

batigoal комментирует...

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

bura комментирует...

Не совсем понятно про increment.. Что значит: "которые являются уникальными только если никакой другой процесс не добавляет данные в ту же самую таблицу"? Т.е. Вы хотите сказать что данная стратегия генерации идентификаторов не потокобезопасна, так?

Pavel Samolisov комментирует...

Инкремент работает следующим образом:

У вас есть PK равный, например, 5. Добавляем параллельно две записи: соответственно для одной будет сгенерирован PK равный 6 и для другой - тоже равный 6. Действительно, инкремент непотокобезопасен.

bura комментирует...

Это верно только при реализации алгоритма на уровне ORM. Но большая часть DB поддерживают инкрементную генерацию и идентификатор записи генерируется на уровне уровне базы. А следовательно, формула DB (с поддержкой инкремента) + ORM = вполне себе рабочий вариант.

Pavel Samolisov комментирует...

В документации описан наиболее общий случай. Как я понял, Hibernate в данном случае не обращается к СУБД для генерации идентификатора, а генерирует его самостоятельно. Отсюда и проблемы.

bura комментирует...

В общем случае так и есть. Но все же, если используется БД с вышеупомянутыми возможностями, то все будет нормально, т.к. сама БД будет следить за целостностью и просто не даст создать запись с уже имеющимся идентификатором.

Pavel Samolisov комментирует...

Ну любая нормальная СУБД должна следить за уникальностью PK. Я думал, что мы обсуждали немножко другое - генерацию уникального PK. Если бы Hibernate делегировал это СУБД, то тогда проблем возможно бы не было (зависит от СУБД), но т.к. он этого в режиме increment не делает, то данный способ потенциально опасен.

bura комментирует...

Но то что этот вариант не стоит использовать - это безусловно ДА. Есть ряд случаев, в которых его можно использовать, но делать это нужно если знаешь все последствия, в противном случае лучше не стоит.

bura комментирует...

"но т.к. он этого в режиме increment не делает" - он это делает, но не для всех БД

Отправить комментарий

Любой Ваш комментарий важен для меня, однако, помните, что действует предмодерация. Давайте уважать друг друга!