Hibernate, @SequenceGenerator и allocSize
Мы все знаем поведение по умолчанию для Hibernate при использовании @SequenceGenerator
- он увеличивает реальную последовательность базы данных на один, несколько это значение на 50 (значение по умолчанию allocationSize
) - и затем использует это значение как идентификатор объекта.
Это неправильное поведение и конфликтует с спецификацией, в котором говорится:
allocSize - (Необязательно) Сумма для увеличения при выделении порядковых номеров из последовательности.
Чтобы быть ясным: я не беспокоюсь о пробелах между сгенерированными идентификаторами.
Мне не нравятся идентификаторы не согласованы с базовой базой данных. Например: любое другое приложение (например, использующее простой JDBC) может захотеть вставить новые строки под идентификаторами, полученными из последовательности, но все эти значения могут уже использоваться Hibernate! Безумие.
Кто-нибудь знает какое-либо решение этой проблемы (не устанавливая allocationSize=1
и тем самым снижая производительность)?
EDIT:
Чтобы все было ясно.
Если в последней вставленной записи был ID = 1
, то HB использует значения 51, 52, 53...
для своих новых объектов, но в то же время: значение последовательности в базе данных будет установлено на 2
. Это может привести к ошибкам, когда другие приложения используют эту последовательность.
В руке: спецификация говорит (в моем понимании), что последовательность базы данных должна быть установлена на 51
, и тем временем HB должен использовать значения из диапазона 2, 3 ... 50
ОБНОВЛЕНИЕ:
Как упоминал Стив Эберсол: поведение, описанное мной (а также наиболее интуитивное для многих), можно включить, установив hibernate.id.new_generator_mappings=true
.
Спасибо всем вам.
ОБНОВЛЕНИЕ 2:
Для будущих читателей ниже вы можете найти рабочий пример.
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "USERS_SEQ")
@SequenceGenerator(name = "USERS_SEQ", sequenceName = "SEQUENCE_USERS")
private Long id;
}
persistence.xml
<persistence-unit name="testPU">
<properties>
<property name="hibernate.id.new_generator_mappings" value="true" />
</properties>
</persistence-unit>
Ответы
Ответ 1
Чтобы быть абсолютно ясным... то, что вы описываете, никак не противоречит спецификации. Спецификация говорит о значениях, которые Hibernate присваивает вашим сущностям, а не значениям, фактически сохраненным в последовательности базы данных.
Однако есть возможность получить поведение, которое вы ищете. Сначала посмотри мой ответ на Есть ли способ динамически выбирать стратегию @GeneratedValue с помощью аннотаций JPA и Hibernate? Это даст вам основы. Пока вы настроены на использование этого SequenceStyleGenerator, Hibernate будет интерпретировать allocationSize
с помощью "объединенного оптимизатора" в SequenceStyleGenerator. "Объединенный оптимизатор" предназначен для использования с базами данных, которые позволяют использовать "приращение" при создании последовательностей (не все базы данных, поддерживающие последовательности, поддерживают прирост). В любом случае, ознакомьтесь с различными стратегиями оптимизации.
Ответ 2
allocationSize=1
Это микро-оптимизация перед получением запроса. Hibernate пытается присвоить значение в диапазоне allocSize и поэтому старайтесь избегать запросов к базе данных для последовательности. Но этот запрос будет выполняться каждый раз, если вы установите его на 1. Это вряд ли имеет значение, поскольку, если к вашей базе данных обращается какое-либо другое приложение, тогда он будет создавать проблемы, если один и тот же идентификатор используется другим приложением.
Следующее поколение Идентификатора последовательности основано на allocSize.
По defualt он сохраняется как 50
, что слишком много. Это также поможет только в том случае, если у вас будет около записей 50
за один сеанс, которые не сохраняются и которые будут сохраняться с использованием этого конкретного сеанса и трансации.
Поэтому вы всегда должны использовать allocationSize=1
при использовании SequenceGenerator
. Что касается большинства базовых последовательностей баз данных, всегда увеличивается на 1
.
Ответ 3
Стив Эберсоле и другие участники,
Не могли бы вы объяснить причину id с большим разрывом (по умолчанию 50)?
Я использую Hibernate 4.2.15 и нашел следующий код в org.hibernate.id.enhanced.OptimizerFactory cass.
if ( lo > maxLo ) {
lastSourceValue = callback.getNextValue();
lo = lastSourceValue.eq( 0 ) ? 1 : 0;
hi = lastSourceValue.copy().multiplyBy( maxLo+1 );
}
value = hi.copy().add( lo++ );
Всякий раз, когда он попадает внутрь инструкции if, значение hi становится намного больше. Таким образом, мой идентификатор во время тестирования с частым перезапуском сервера генерирует следующие идентификаторы последовательности:
1, 2, 3, 4, 19, 250, 251, 252, 400, 550, 750, 751, 752, 850, 1100, 1150.
Я знаю, что вы уже сказали, что это не противоречит спецификации, но я считаю, что это будет очень неожиданная ситуация для большинства разработчиков.
Любой вход будет очень полезен.
Jihwan
UPDATE:
ne1410s: Спасибо за редактирование.
cfrick: ОК. Я сделаю это. Это был мой первый пост здесь и не был уверен, как его использовать.
Теперь я лучше понял, почему maxLo использовался для двух целей: поскольку hibernate вызывает последовательность DB один раз, продолжайте увеличивать идентификатор на уровне Java и сохраняйте его в БД, значение идентификатора уровня Java должно учитывать, сколько было измененный без вызова последовательности БД, когда он вызывает последовательность в следующий раз.
Например, идентификатор последовательности был 1 в точке, а спящий режим - 5, 6, 7, 8, 9 (с allocSize = 5). В следующий раз, когда мы получим следующий порядковый номер, DB возвращает 2, но hibernate должен использовать 10, 11, 12... Поэтому, "hi = lastSourceValue.copy(). MultiplyBy (maxLo + 1)" используется для получения следующего идентификатора 10 из 2, возвращаемых из последовательности БД. Кажется, что это было всего лишь во время частого перезапуска сервера, и это было моей проблемой с большим разрывом.
Итак, когда мы используем ID SEQUENCE, вставленный идентификатор в таблице не будет соответствовать номеру SEQUENCE в БД.
Ответ 4
После преобразования в спящий исходный код и
Ниже конфигурация переходит к Oracle db для следующего значения после 50 вставок. Так что каждый раз, когда он вызывается, каждый раз приращение INST_PK_SEQ.
Спящий режим 5 используется для стратегии ниже
Проверьте также ниже
http://docs.jboss.org/hibernate/orm/5.1/userguide/html_single/Hibernate_User_Guide.html#identifiers-generators-sequence
@Id
@Column(name = "ID")
@GenericGenerator(name = "INST_PK_SEQ",
strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",
parameters = {
@org.hibernate.annotations.Parameter(
name = "optimizer", value = "pooled-lo"),
@org.hibernate.annotations.Parameter(
name = "initial_value", value = "1"),
@org.hibernate.annotations.Parameter(
name = "increment_size", value = "50"),
@org.hibernate.annotations.Parameter(
name = SequenceStyleGenerator.SEQUENCE_PARAM, value = "INST_PK_SEQ"),
}
)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "INST_PK_SEQ")
private Long id;
Ответ 5
Я бы проверил DDL для последовательности в схеме. Реализация JPA отвечает только за создание последовательности с правильным размером распределения. Поэтому, если размер распределения равен 50, то ваша последовательность должна иметь прирост в 50 в DDL.
Этот случай обычно может возникать при создании последовательности с размером распределения 1, а затем с настройкой на размер 50 (или по умолчанию) распределения, но DDL последовательности не обновляется.
Ответ 6
Я тоже сталкивался с этой проблемой в Hibernate 5:
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = SEQUENCE)
@SequenceGenerator(name = SEQUENCE, sequenceName = SEQUENCE)
private Long titId;
Получил предупреждение, как это ниже:
Обнаружено использование устаревшего генератора идентификаторов на основе последовательностей [org.hibernate.id.SequenceHiLoGenerator]; используйте взамен org.hibernate.id.enhanced.SequenceStyleGenerator. Подробнее см. В Руководстве по отображению модели домена Hibernate.
Затем изменил мой код на SequenceStyleGenerator:
@Id
@GenericGenerator(name="cmrSeq", strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",
parameters = {
@Parameter(name = "sequence_name", value = "SEQUENCE")}
)
@GeneratedValue(generator = "sequence_name")
private Long titId;
Это решило мои две проблемы:
- Устаревшее предупреждение исправлено
- Теперь идентификатор генерируется в соответствии с последовательностью оракула.