Hibernate、@ SequenceGenerator、allocationSize


117

使用時のHibernateのデフォルトの動作は誰でも知っています@SequenceGenerator-実際のデータベースシーケンスを1増やし、この値を50(デフォルトallocationSize値)倍します-そして、この値をエンティティIDとして使用します。

これは不正な動作であり、次の仕様と競合します。

allocationSize-(オプション)シーケンスからシーケンス番号を割り当てるときに増分する量。

明確にするために:生成されたID間のギャップについては気にしません。

基になるデータベースシーケンスと一致ない IDに関心があります。たとえば、他のアプリケーション(プレーンなJDBCを使用するなど)は、シーケンスから取得したIDの下に新しい行を挿入したい場合がありますが、これらの値はすべてHibernateですでに使用されている場合があります。狂気。

誰かがこの問題の解決策を知っていますか(設定allocationSize=1してパフォーマンスを低下させることなく)?

編集:
物事を明確にするために。最後に挿入されたレコードにID =があった1場合、HBは51, 52, 53...新しいエンティティの値を同時に使用しますが、データベース内のシーケンスの値はに設定され2ます。これは、他のアプリケーションがそのシーケンスを使用しているときに簡単にエラーにつながる可能性があります。

反対に、仕様では、(私の理解では)データベースシーケンスが設定されている必要が51あり、その間HBは範囲の値を使用する必要があると述べています 2, 3 ... 50


更新:
Steve Ebersoleが以下で述べたように:私が説明した動作(および多くの人にとって最も直感的な動作)は、を設定することで有効にできます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>

2
「allocationSize = 1を設定せずに、performancを低下させることなしに」パフォーマンスを低下させる理由1に設定するのはなぜですか?
sheidaei

3
@sheidaeiは以下のコメントを参照してください:-)これは、すべての人saveがシーケンスの次の値についてデータベースをクエリする必要があるためです。
G.デメッキ

同じ問題に直面していただきありがとうございます。最初は@SequenceGeneratorごとにallocationSize = 1を追加していました。hibernate.id.new_generator_mappings = trueを使用すると、それを防止できます。JPAは引き続き各クエリのIDを取得するためにデータベースにクエリを
発行し

1
SequenceGeneratorIDの量がで指定されたときに休止状態にのみデータベースを照会しますallocationsize無くなっ。セットアップした場合allocationSize = 1、それがHibernateが各挿入についてDBにクエリする理由です。この値を変更すれば完了です。
G.デメッキ16年

1
ありがとう!hibernate.id.new_generator_mappings設定はとても重要です。ID番号がワイルドになる理由を調査するのにそれほど時間をかける必要がないのが、デフォルトの設定であることを願っています。
LeOn-Han Li

回答:


43

完全に明確にするために...あなたが説明する内容は、仕様と決して矛盾しませ。仕様では、Hibernateがエンティティに割り当てる値について説明しており、実際にデータベースシーケンスに格納されている値ではありません。

ただし、探している動作を取得するオプションがあります。まず、JPAアノテーションとHibernateを使用して@GeneratedValue戦略を動的に選択する方法はありますか? それはあなたに基本を与えるでしょう。そのSequenceStyleGeneratorを使用するように設定されている限り、HibernateはSequenceStyleGenerator allocationSizeの「プールされたオプティマイザ」を使用して解釈します。「プールされたオプティマイザ」は、シーケンスの作成で「増分」オプションを許可するデータベースで使用するためのものです(シーケンスをサポートするすべてのデータベースが増分をサポートしているわけではありません)。とにかく、そこでさまざまなオプティマイザ戦略について読んでください。


スティーブに感謝!最良の答え。また、あなたの他の投稿は役に立ちました。
G. Demecki

4
あなたがの共著者であることにも気付きましたorg.hibernate.id.enhanced.SequenceStyleGenerator。あなたは私を驚かせた。
G. Demecki

22
どのようにあなたを驚かせましたか?私はHibernateの主要開発者です。多くのHibernateクラスを書いたり、共同で書いたりしています;)
Steve Ebersole

参考までに。大きなギャップを防ぐために、DBシーケンスの増分は避けてください。IDキャッシュが不足すると、DBシーケンスにallocationSizeが乗算されます。詳細は、stackoverflow.com
questions / 5346147 /…

1
グローバルに使用される「オプティマイザー」を変更する1つの方法は、休止状態オプションに次のようなものを追加することです。LegacyHiLoAlgorithOptimizerの代わりに、任意のオプティマイザクラスを選択でき、それがデフォルトになります。これにより、すべての注釈を変更することなく、必要な動作をデフォルトとして維持しやすくなります。さらに、「pooled」および「hilo」オプティマイザに注意してください。これらは、シーケンス値が0で始まるときに奇妙な結果を与え、負のIDを引き起こします。
fjalvingh

17

allocationSize=1これは、クエリを取得する前のマイクロ最適化です。Hibernateは、allocationSizeの範囲の値を割り当てようとするため、データベースにシーケンスをクエリしないようにします。しかし、このクエリは、1に設定すると毎回実行されます。データベースが他のアプリケーションからアクセスされた場合、別のアプリケーションが同じIDを使用していると問題が発生するため、ほとんど違いはありません。

次世代のシーケンスIDは、allocationSizeに基づいています。

デフォルトでは、50それは多すぎるとして保持されます。また50、1つのセッションで、永続化されておらず、この特定のセッションと変換を使用して永続化されるレコードがほぼある場合にのみ役立ちます。

したがって、を使用allocationSize=1してSequenceGeneratorいる間は常に使用する必要があります。基礎となるデータベースのほとんどについては、シーケンスは常にによって増分され1ます。


12
パフォーマンスとは関係ありませんか?あなたは本当に確か?allocationSize=1Hibernateでは、すべてのsave操作で新しいID値を取得するためにデータベースにアクセスする必要があると教えられています。
G. Demecki

2
これは、クエリを取得する前のマイクロ最適化です。Hibernateは範囲内の値を割り当てよallocationSizeうとするため、データベースにシーケンスをクエリしないようにします。しかし、このクエリは、1に設定すると毎回実行されます。データベースが他のアプリケーションによってアクセスされている場合、別のアプリケーションによって同じIDが使用されていると問題が発生するため、これはほとんど違いがありません
Amit Deshpande

そして、はい、割り当てサイズ1が実際のパフォーマンスに影響を与えるかどうかは、完全にアプリケーション固有です。もちろん、マイクロベンチマークでは、それは常に大きな影響として現れます。それはほとんどのベンチマーク(マイクロまたはその他)の問題ですが、それらは単に現実的ではありません。そして、それらがある程度現実的であるほど十分に複雑であっても、ベンチマークの結果が実際のアプリケーションにどれだけ近いかを見て、ベンチマークの結果がアプリに表示される結果にどの程度適用できるかを理解する必要があります。短い話です。自分で試してみてください
Steve Ebersole

2
OK。すべてがアプリケーション固有ですよね!アプリケーションが読み取り専用アプリケーションの場合、割り当てサイズ1000または1を使用することによる影響は絶対に0です。一方、これらのようなものはベストプラクティスです。ベストプラクティスを尊重しないと、それらが集まり、アプリケーションのパフォーマンスが低下します。もう1つの例は、トランザクションが絶対に必要ないときにトランザクションを開始することです。
Hasan Ceylan

1

Steve Ebersole&他のメンバー、
より大きなギャップ(デフォルトでは50)のIDの理由を親切に説明していただけませんか?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の値は非常に大きくなります。したがって、サーバーを頻繁に再起動するテスト中のIDは、1、2、3、4、19、250、251、252、400、550、750、751、752、850、1100、1150のシーケンスIDを生成します

仕様と矛盾しなかったと既におっしゃっていましたが、ほとんどの開発者にとってこれは非常に予期しない状況になると思います。

誰の入力も大いに役立ちます。

ジファン

更新:ne1410s:編集ありがとうございます。
cfrick:OK。私はそれを行います。初めての投稿でしたが、使い方がわかりませんでした。

今、私はmaxLoが2つの目的で使用された理由をよりよく理解しました:hibernateがDBシーケンスを一度呼び出し、JavaレベルでIDを増やし続け、それをDBに保存するため、JavaレベルのID値は、呼び出しなしで変更された量を考慮する必要があります次回シーケンスを呼び出すときのDBシーケンス。

たとえば、シーケンスIDはある時点で1であり、Hibernateは5、6、7、8、9を入力しました(allocationSize = 5)。次回、次のシーケンス番号を取得すると、DBは2を返しますが、hibernateは10、11、12を使用する必要があります。そのため、「hi = lastSourceValue.copy()。multiplyBy(maxLo + 1)」はDBシーケンスから返された2から次のID 10を取得するために使用されます。気になるのは、頻繁なサーバーの再起動時だけでした。これは、ギャップが大きいという私の問題でした。

したがって、SEQUENCE IDを使用する場合、テーブルに挿入されたIDはDBのSEQUENCE番号と一致しません。


1

hibernateのソースコードと以下の構成を掘り下げた後、50回の挿入後、次の値をOracle dbに渡します。したがって、呼び出されるたびに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
申し訳ありませんが、これは何かを設定する非常に詳細な方法です。Hibernate全体、つまりすべてのエンティティに対して2つのパラメータを使用して簡単に表現できます。
G. Demecki 2016年

trueですが、他の方法で試したところ、機能している場合はどれも機能しませんでした。設定した方法を送信できます
fatih tekin

私は私の答えを更新しました-今それはまた実用的な例が含まれています。上記の私のコメントは部分的に間違っていますが、残念ながら、すべてのエンティティに対してグローバルに設定することallocationSizeも、initialValueグローバルに設定することもできません(ジェネレーターを1つだけ使用しない限り、非常に読みにくいです)。
G.デメッキ16年

1
説明ありがとうございますが、あなたが上で書いたものは試してみましたが、Hibernate 5.0.7で動作しませんでした。最終バージョンでは、この目標を達成できるようにソースコードを掘り下げました。これは、私が見つけた実装です。休止状態のソースコード。構成が悪いように見えるかもしれませんが、残念なことにhibernates apiであり、hibernateの標準のEntityManager実装を使用しています
fatih tekin

1

私もHibernate 5でこの問題に直面しました。

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = SEQUENCE)
@SequenceGenerator(name = SEQUENCE, sequenceName = SEQUENCE)
private Long titId;

以下のような警告が表示されました:

非推奨の[org.hibernate.id.SequenceHiLoGenerator]シーケンスベースのIDジェネレーターの使用が見つかりました。代わりに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;

これは私の2つの問題を解決しました:

  1. 非推奨の警告が修正されました
  2. これで、Oracleシーケンスに従ってIDが生成されます。

0

スキーマのシーケンスのDDLをチェックします。JPA実装は、正しい割り当てサイズのシーケンスの作成のみを担当します。したがって、割り当てサイズが50の場合、シーケンスのDDLは50ずつ増加する必要があります。

このケースは通常、割り当てサイズ1のシーケンスを作成し、後で割り当てサイズ50(またはデフォルト)に構成した場合に発生しますが、シーケンスDDLは更新されません。


あなたは私のポイントを誤解しています。ALTER SEQUENCE ... INCREMENTY BY 50;問題は同じままなので、何も解決しません。シーケンス値はまだ実際のエンティティIDを反映していません。
G. Demecki

ここで問題をよりよく理解できるように、テストケースを共有してください。
Hasan Ceylan 2012年

1
テストケース?どうして?私が投稿した質問はそれほど複雑ではなく、すでに回答されています。HiLoジェネレーターがどのように機能するかを知らないようです。とにかく、あなたの時間と努力を犠牲にしてくれてありがとう。
G.デメッキ2012年

1
グレゴリー、私は実際に私が話していることを知っています、私は現在インキュベーション中の%100 JPA実装であるバトゥーJPAを書きました。一方、私はあなたの質問を誤解している可能性があり、シーケンスでHibernateを使用することで問題が発生するとはまったく思いませんでした。重要なのは、あなたが質問に対する解決策を得たことです。申し訳ありませんが、正解としてマークされた回答を逃しました...
Hasan Ceylan

すみません、あなたを怒らせるつもりはありませんでした。ご協力いただきありがとうございます。質問に回答します。
G. Demecki
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.