@SequenceGenerator
を使用するときのHibernateのデフォルトの動作はわかっています。実際のデータベースシーケンスをone増やし、この値を50倍にします(デフォルトallocationSize
value)-次に、この値をエンティティIDとして使用します。
これは不正な動作であり、 specification と競合する:
allocationSize-(オプション)シーケンスからシーケンス番号を割り当てるときに増分する量。
明確にするために:生成されたID間のギャップについては気にしません。
基盤となるデータベースシーケンスと一貫性のないIDが重要です。例:他のアプリケーション(プレーンJDBCなど)は、シーケンスから取得したIDの下に新しい行を挿入したい場合がありますが、これらの値はすべてHibernateですでに使用されている可能性があります!狂気。
誰かがこの問題の解決策を知っていますか(allocationSize=1
を設定してパフォーマンスを低下させることなく)?
編集:
物事を明確にする。最後に挿入されたレコードのIDが1
の場合、HBはその新しいエンティティに値51, 52, 53...
を使用しますが、同時にデータベースのシーケンスの値は2
に設定されます。他のアプリケーションがそのシーケンスを使用している場合、エラーが発生しやすくなります。
一方、仕様では、(私の理解では)データベースシーケンスは51
に設定されるべきであり、その間にHBは2, 3 ... 50
の範囲の値を使用する必要があると述べています。
UPDATE:
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>
絶対に明確にするために...あなたが記述することはnotが何らかの形で仕様と矛盾します。この仕様では、データベースシーケンスに実際に格納されている値ではなく、Hibernateがエンティティに割り当てる値について説明しています。
ただし、探している動作を取得するオプションがあります。最初に JPAアノテーションとHibernateを使用して@GeneratedValue戦略を動的に選択する方法はありますか? これで基本がわかります。そのSequenceStyleGeneratorを使用するように設定されている限り、HibernateはSequenceStyleGeneratorの「プールされたオプティマイザー」を使用してallocationSize
を解釈します。 「プールオプティマイザー」は、シーケンスの作成時に「増分」オプションを許可するデータベースで使用します(シーケンスをサポートするすべてのデータベースが増分をサポートするわけではありません)。とにかく、そこのさまざまなオプティマイザ戦略について読んでください。
allocationSize=1
これは、クエリを取得する前のマイクロ最適化です。Hibernateは、allocationSizeの範囲で値を割り当てようとするため、データベースでのシーケンスのクエリを回避しようとします。しかし、このクエリは1に設定すると毎回実行されます。データベースに他のアプリケーションがアクセスすると、同じIDが別のアプリケーションで使用されると問題が発生するため、ほとんど違いはありません。
シーケンスIDの次世代は、allocationSizeに基づいています。
デフォルトでは、50
として保持されますが、これは多すぎます。また、1つのセッションに約50
のレコードが保持されず、この特定のセッションとトランザクションを使用して保持される場合にのみ役立ちます。
したがって、SequenceGenerator
を使用するときは、常にallocationSize=1
を使用する必要があります。基になるデータベースのほとんどについては、シーケンスは常に1
ずつ増加します。
休止状態のソースコードを掘り下げた後、以下の構成は、50回の挿入後の次の値のためにOracle dbに移動します。したがって、INST_PK_SEQが呼び出されるたびにINST_PK_SEQを50ずつ増やしてください。
Hibernate 5は以下の戦略に使用されます
@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;
スティーブ・エバーソールと他のメンバー、
ギャップが大きい(デフォルトでは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は、次のシーケンスIDを生成します。
1、2、3、4、19、250、251、252、400、550、750、751、752、850、1100、1150。
仕様と矛盾することはないと既に言っていましたが、ほとんどの開発者にとってこれは非常に予想外の状況になると思います。
誰の入力も大いに役立ちます。
じわん
更新:ne1410s:編集してくれてありがとう。
cfrick:OK。私はそれを行います。ここでの最初の投稿であり、使用方法がわかりませんでした。
ここで、maxLoが2つの目的で使用された理由をよりよく理解しました。休止状態はDBシーケンスを1回呼び出すため、JavaレベルのIDを増やし続け、DB、Javaに保存します_レベルID値は、次にシーケンスを呼び出すときにDBシーケンスを呼び出さずに変更された量を考慮する必要があります。
たとえば、ある時点でシーケンスIDは1であり、休止状態は5、6、7、8、9(allocationSize = 5)を入力しました。次回、次のシーケンス番号を取得すると、DBは2を返しますが、休止状態では10、11、12を使用する必要があります。そのため、「hi = lastSourceValue.copy()。multiplyBy(maxLo + 1)」はDBシーケンスから返された2から次のID 10を取得するために使用されます。頻繁にサーバーを再起動しているときに気になったのは唯一の問題であり、これは大きなギャップの問題でした。
したがって、SEQUENCE IDを使用すると、テーブルに挿入されたIDはDBのSEQUENCE番号と一致しません。
スキーマ内のシーケンスのDDLを確認します。 JPA実装は、正しい割り当てサイズのシーケンスの作成のみを担当します。したがって、割り当てサイズが50の場合、シーケンスのDDLの増分は50でなければなりません。
このケースは通常、割り当てサイズ1のシーケンスの作成で発生し、後で割り当てサイズ50(またはデフォルト)に設定されますが、シーケンスDDLは更新されません。
私も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 Domain Model Mapping Guideを参照してください。
次に、コードを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つの問題が解決しました。