Hibernate, @SequenceGenerator and distributionSize


117

Всі ми знаємо поведінку Hibernate за замовчуванням при використанні @SequenceGenerator- вона збільшує реальну послідовність баз даних на одиницю , кратне це значення на 50 (значення за замовчуванням allocationSize) - і потім використовує це значення як ідентифікатор сутності.

Це неправильна поведінка та суперечить специфікації, яка говорить:

dodjeleSize - (необов'язково) Сума, що збільшується при розподілі порядкових номерів із послідовності.

Щоб було зрозуміло: я не переймаюся розбіжностями між створеними ідентифікаторами.

Я дбаю про ідентифікатори, які не відповідають основній послідовності бази даних. Наприклад: будь-яка інша програма (яка, наприклад, використовує звичайний JDBC), може захотіти вставити нові рядки під ідентифікаторами, отриманими з послідовності, - але всі ці значення вже можуть використовувати Hibernate! Божевілля.

Хтось знає якесь рішення цієї проблеми (без встановлення allocationSize=1і, таким чином, погіршення продуктивності)?

EDIT:
Щоб все було зрозуміло. Якщо останній вставлений запис мав ID = 1, то НВ 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;
}

стійкість.xml

<persistence-unit name="testPU">
  <properties>
    <property name="hibernate.id.new_generator_mappings" value="true" />
  </properties>
</persistence-unit>

2
"без встановлення distributionSize = 1 і тим самим погіршує performanc", чому це погіршує продуктивність, якщо ви встановите його на 1?
sheidaei

3
@sheidaei див. може прокоментувати нижче :-) Це тому, що кожен saveповинен запитувати базу даних для наступного значення послідовності.
Г. Демецький

Дякую, що ви стикалися з тим же питанням. Спочатку я додавав dodipize = 1 на кожному @SequenceGenerator. Використання hibernate.id.new_generator_mappings = true запобігає цьому. Хоча JPA все ще
запитує

1
Завдяки SequenceGeneratorHibernate буде запитувати базу даних лише тоді, коли кількість ідентифікаторів, визначених за допомогою, allocationsizeзакінчується. Якщо ви налаштували, allocationSize = 1то це причина, чому сплячий запитує БД для кожної вставки. Змініть це значення, і ви закінчите.
Г. Демецький

1
Дякую! hibernate.id.new_generator_mappingsустановка дійсно важливо. Я би сподівався, що це налаштування за замовчуванням, що мені не доведеться витрачати стільки часу на дослідження, чому ідентифікаційний номер стає дивним.
LeOn - Хан Лі

Відповіді:


43

Щоб бути абсолютно зрозумілим ... те, що ви описуєте, жодним чином не суперечить специфікації. Специфікація розповідає про значення, які Hibernate призначає вашим особам, а не про значення, які фактично зберігаються в послідовності бази даних.

Однак є можливість отримати поведінку, яку ви шукаєте. Спочатку дивіться мою відповідь на тему: Чи є спосіб динамічно вибрати стратегію @GeneratedValue, використовуючи анотації JPA та сплячку? Це дасть вам основи. Поки ви налаштовані на використання цього SequenceStyleGenerator, Hibernate буде інтерпретувати, allocationSizeвикористовуючи "об'єднаний оптимізатор" у SequenceStyleGenerator. "Об'єднаний оптимізатор" призначений для використання з базами даних, які дозволяють опцію "збільшення" для створення послідовностей (не всі бази даних, що підтримують послідовності, збільшують приріст). У будь-якому разі, прочитайте про різні стратегії оптимізатора там.


Дякую Стів! Найкраща відповідь. Також корисна була ваша інша публікація .
Г. Демецький

4
Я також помітив, що ви є співавтором org.hibernate.id.enhanced.SequenceStyleGenerator. Ти мене здивував.
Г. Демецький

22
Здивували вас, як? Я провідний розробник Hibernate. Я написав / співавторив багато класів зі сплячки;)
Стів Еберсол

Просто для запису. Слід уникати збільшення послідовності БД, щоб запобігти великим розривам. Послідовність DB множиться allocationSize , коли ID працює кеш out.More деталі stackoverflow.com/questions/5346147 / ...
Olcay Tarazan

1
Один із способів змінити "оптимізатор", що використовується у всьому світі, - додати щось подібне до своїх сплячих параметрів: serviceBuilder.applySetting ("hibernate.id.optimizer.pooled.preferred", LegacyHiLoAlgorithmOptimizer.class.getName ()); Замість LegacyHiLoAlgorithOptimizer ви можете вибрати будь-який клас оптимізатора, і він стане за замовчуванням. Це повинно полегшити збереження поведінки, яку ви хочете, як за замовчуванням, не змінюючи всіх приміток. Крім того, слідкуйте за оптимізаторами "об'єднаного" та "hilo": вони дають незвичайні результати, коли значення послідовності починається з 0, викликаючи негативні ідентифікатори.
fjalvingh

17

allocationSize=1Це мікрооптимізація, перш ніж отримувати запит Hibernate намагається призначити значення в діапазоні dodjeleSize і тому намагайтеся уникати бази запитів для послідовності. Але цей запит буде виконуватися щоразу, якщо ви встановите його на 1. Це навряд чи має значення, оскільки якщо до вашої бази даних звертається якась інша програма, то це створить проблеми, якщо той самий ідентифікатор тим часом використовується іншим додатком.

Наступне покоління Id послідовності засноване на rasporedSize.

За замовчуванням він зберігається так, як 50це занадто багато. Це також допоможе лише у тому випадку, якщо у вас буде близько 50записів за один сеанс, які не зберігаються і які будуть зберігатися за допомогою цього конкретного сеансу та трансляції.

Тому завжди слід користуватися allocationSize=1під час використання SequenceGenerator. Що стосується більшості базових баз даних, послідовність завжди збільшується на 1.


12
Немає нічого спільного з продуктивністю? Ви справді впевнені? Мене вчили, що зі allocationSize=1сплячим режимом для кожної saveоперації потрібно здійснити поїздку до бази даних, щоб отримати нове значення ідентифікатора.
Г. Демецький

2
Це мікрооптимізація, перш ніж отримувати запит Hibernate намагається присвоїти значення в діапазоні, allocationSizeі тому намагайтеся уникати запитів бази даних щодо послідовності. Але цей запит буде виконуватися щоразу, якщо ви встановите його на 1. Це навряд чи має значення, оскільки якщо до вашої бази даних буде доступний якийсь інший додаток, він створить проблеми, якщо той самий ідентифікатор тим часом використовується іншим додатком
Amit Deshpande

І так, це абсолютно конкретне додаток, чи має розмір 1 виділення реальний вплив на ефективність. Звичайно, у мікро-орієнтирі це завжди буде виявлятися як величезний вплив; ось проблема в більшості еталонів (мікро чи іншим чином), вони просто не реалістичні. І навіть якщо вони досить складні, щоб бути дещо реалістичними, ви все одно повинні переглянути, наскільки тісний показник до вашої фактичної програми, щоб зрозуміти, наскільки застосовні результати еталону до результатів, які ви побачили б у вашому додатку. Довга коротка історія .. випробуй на собі
Стів Еберсоль

2
ГАРАЗД. Все конкретно для додатків, чи не так! Якщо ваша програма є програмою лише для читання, вплив використання розміру виділення 1000 або 1 абсолютно 0. З іншого боку, такі речі є найкращими методами. Якщо ви не дотримуєтесь найкращих практик, які вони збирають, і комбінований вплив буде вашою заявою стає млявою. Іншим прикладом може бути започаткування транзакції, коли вона вам абсолютно не потрібна.
Хасан Джейлан

1

Стів Еберсол та інші члени,
Чи ласкаво поясніть причину ідентифікатора з більшим розривом (за замовчуванням 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, значення привіт стає набагато більшим. Отже, мій ідентифікатор під час тестування при частому перезапуску сервера генерує такі ідентифікатори послідовності:
1, 2, 3, 4, 19, 250, 251, 252, 400, 550, 750, 751, 752, 850, 1100, 1150.

Я знаю, ви вже сказали, що це не суперечить специфікації, але я вважаю, що це буде дуже несподіваною ситуацією для більшості розробників.

Будь-який внесок буде дуже корисним.

Jihwan

ОНОВЛЕННЯ: ne1410s: Дякую за редагування.
cfrick: Гаразд. Я зроблю це. Це був мій перший пост тут і не знав, як ним користуватися.

Тепер я краще зрозумів, чому maxLo використовувався для двох цілей: Оскільки сплячий режим викликає послідовність БД один раз, продовжуйте збільшувати ідентифікатор на рівні Java і зберігає його в БД, значення ідентифікатора рівня Java повинно враховувати, наскільки змінено без виклику послідовність БД, коли вона викликає послідовність наступного разу.

Наприклад, ідентифікатор послідовності становив 1 у точці, і в сплячку ввели 5, 6, 7, 8, 9 (з виділенням розміру = 5). Наступного разу, коли ми отримаємо наступний номер послідовності, БД повертає 2, але в сплячку потрібно використовувати 10, 11, 12 ... Отже, саме тому "привіт = lastSourceValue.copy (). MultiplyBy (maxLo + 1)" використовується для отримання наступного id 10 з 2, повернутого з послідовності БД. Здається, що турбує лише те, що було часто перезапускати сервер, і це було моїм питанням із більшим розривом.

Отже, коли ми використовуємо ідентифікатор SEQUENCE, вставлений ідентифікатор в таблиці не збігатиметься з номером SEQUENCE в БД.


1

Після заглиблення у сплячий вихідний код і нижче конфігурація переходить до Oracle db для наступного значення після 50 вставок. Тому зробіть свій INST_PK_SEQ приріст 50 щоразу, коли він буде викликаний.

Hibernate 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;

3
Вибачте, але це надзвичайно багатослівний спосіб налаштування чогось, який можна легко виразити двома параметрами для всієї сплячки та таким чином для всіх сутностей.
Г. Демецький

правда, але коли я намагаюся з іншими способами, жоден з них не працював, якщо у вас це працює, ви можете надіслати мені, як ви налаштували
fatih tekin

Я оновив свою відповідь - тепер вона включає також робочий приклад. Хоча мій коментар вище частково помилковий: на жаль, ви не можете встановити allocationSizeні initialValueглобально, ні для всіх сутностей (якщо тільки не використовувати один генератор, але IMHO не дуже читабельний).
Г. Демецький

1
Дякую за пояснення, але те, що ви написали вище, я спробував, і це не працювало зі сплячим режимом 5.0.7.Закінчена версія, тоді я занурився у вихідний код, щоб мати змогу досягти цієї мети, і це реалізація, яку я зміг знайти у сплячому коді. Налаштування може виглядати погано, але це, на жаль, зимує api, і я використовую стандартну програму EntityManager, що перебуває у сплячці
fatih tekin

1

Я теж зіткнувся з цим питанням у сплячому режимі 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. Детальні відомості див. У Посібнику з картографування моделі гібернаційного домену.

Потім змінив мій код на 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. Тепер ідентифікатор генерується відповідно до послідовності oracle.

0

Я перевірив би DDL на послідовність в схемі. Реалізація JPA відповідає лише за створення послідовності з правильним розміром розподілу. Отже, якщо розмір виділення становить 50, то ваша послідовність повинна мати збільшення 50 у своєму DDL.

Цей випадок, як правило, може виникати при створенні послідовності з розміром 1 розподілу, потім згодом налаштовується на розмір 50 (або за замовчуванням), але послідовність DDL не оновлюється.


Ви нерозумієте мою думку. ALTER SEQUENCE ... INCREMENTY BY 50;нічого не вирішиш, адже проблема як і раніше залишається тією ж. Значення послідовності все ще не відображає ідентифікаторів реальних осіб.
Г. Демецький

Будь ласка, поділіться тестовим випадком, щоб ми могли краще зрозуміти проблему тут.
Хасан Джейлан

1
Тестовий випадок? Чому? Питання, яке я опублікував, було не так вже й складно, і на нього вже відповіли. Здається, ви не знаєте, як працює генератор HiLo. У будь-якому випадку: дякую за те, що ви принесли в жертву ваш час та зусилля.
Г. Демецький

1
Грегорі, насправді я знаю, про що я говорю, я написав Batoo JPA, що є 100-відсотковою реалізацією JPA, яка наразі перебуває в інкубації і б'є в сплячку за швидкістю - в 15 разів швидше. З іншого боку, я, можливо, неправильно зрозумів ваше запитання і не вважав, що використання Hibernate з послідовностями взагалі повинно створювати будь-які проблеми, оскільки я використовував Hibernate з 2003 року в багатьох проектах на багатьох базах даних. Важливо, що ви вирішили питання, вибачте, що я пропустив відповідь, зазначену як правильну ...
Hasan Ceylan

Вибачте, я не хотів вас образити. Ще раз дякую за вашу допомогу, на питання відповіли.
Г. Демецький
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.