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;

Это решило мои две проблемы:

  1. Устаревшее предупреждение исправлено
  2. Теперь идентификатор генерируется в соответствии с последовательностью оракула.