2017-04-24 13 views
0

私は、実稼働用のOracleデータベースと統合テスト用のH2データベースとでHibernate 3.5.6-Finalを使用しています。 ID作成のためのHibernateのマッピングがEasyPersistentObjectを拡張するすべてのエンティティと以下のようになります。私は、データベースからすべてのデータを削除していますそれぞれのJUnit統合テストの前にHibernateシーケンスジェネレータをリセットするには?

@MappedSuperclass 
public class EasyPersistentObject implements Serializable { 

@Id 
@SequenceGenerator(name = "hibernate_seq", sequenceName = "hibernate_id_seq", allocationSize = 1) 
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "hibernate_seq") 
protected Integer id; 

私はインクリメントまで、すべてが正常に動作します

new SchemaExport(configuration).create(false, true); 

で配列生成のためのallocationSize。これを例えばに上げる。 10は、テストデータを挿入するときに、UniqueKeyConstraintViolationsでいくつかのテストを中断します。例えば

  1. のTest1:8つのテストオブジェクト(Hibernateはまだ割り当てられたID値9および10を有している)
  2. 再作成データベース
  3. Test2を作成:12個のテストオブジェクトを作成(Hibernateが9と10を使用しIDについては、一方(1-10)resetedとID 9と第二のエンティティを挿入しようとすると失敗したデータベース配列から新しいIDをロードする)

だから私の質問:各テストの前にHibernateが割り当てたIDをリセットする方法はありますか?

追加:このように作成された私はJPAはなく、純粋にHibernateのSessionFactoryからのPersistenceManagerを使用していない

@Bean(name = "easySF") 
public SessionFactory easySessionFactory(@Qualifier("easyConfig") AnnotationConfiguration configuration) { 
    configuration.setInterceptor(new HibernateInterceptor()); 
    return configuration.buildSessionFactory(); 
} 

@Bean(name = "easyConfig") 
protected AnnotationConfiguration easyHibernateConfiguration() { 
    AnnotationConfiguration configuration = new AnnotationConfiguration(); 
    configuration.setProperties(createHibernateProperties()); 
    configuration.addResource("NamedQueries.hbm.xml"); 
    for (Class annotatedClass : getAnnotatedClasses()) { 
     configuration.addAnnotatedClass(annotatedClass); 
    } 
    configuration.setListener("post-load", new PreloadEventListener()); 
    return configuration; 
} 

私は本当にHibernateのIDジェネレータを取得するために私の全体のSpringコンテキストをリロードする必要がありますかリセットされましたか?これは、そのIDジェネレータリロードする休止状態を強制する

答えて

1

@Beforeメソッドでは、すべてのJUnitテストのシーケンスをリセットすることであるコメントに基づいて

。私たちは、休止状態4.2.7を使用しています。

ハイバネートに深くデバックした後、私はシーケンスジェネレータを拡張しました。我々は、標準的なシーケンスジェネレータを使用してエンティティを作成:

@SequenceGenerator(name = "SomeSeq", sequenceName = "DB_SEQ", allocationSize = 50) 

、Hibernateはエンティティごとにorg.hibernate.id.SequenceHiLoGeneratorを作成します。 SequnceHiLoGeneratorは、OptimizerFactory.LegacyHiLoAlgorithmOptimizerインスタンスに委譲します。

シーケンスカウンタをリセットし、データベースシーケンスと強制的に同期させるには、LegacyHiLoAlgorithmOptimizerの内部変数をリセットする必要があります。残念ながら、これらの変数はprivateであり、変更可能ではありません。私は継承を使って方法を見つけようとしましたが、私は洗練されたソリューションを見つけませんでした。最後に、私は
はSequenceHiLoGeneratorのソースコピーを作成し、簡単なリセット機能とそれを拡張:

@GenericGenerator(name = "SomeSeq", strategy = "yourpackage.ResetableIdGenerator", parameters = { 
     @Parameter(name = "sequence", value = "DB_SEQ"), @Parameter(name = "max_lo", value = "49") }) 

テストわたってるしきシーケンスジェネレータをリセットします。

public class ResetableIdGenerator extends SequenceGenerator { 
       public static int cycle = 0; // global, indicating current test cycle 
       protected int startingCycle = 0; // instance, indicating the test cycle the LegacyHiLoAlgorithmOptimizer was used the last time 
    [...] 
     public synchronized Serializable generate(final SessionImplementor session, Object obj) { 
      // create a new HiLoOptimizer if there's a new test cycle 
      if (startingCycle < cycle) { 
       hiloOptimizer = new OptimizerFactory.LegacyHiLoAlgorithmOptimizer(getIdentifierType().getReturnedClass(), 
         maxLo); 
       startingCycle = cycle; 
      } 
[....] 

カスタム・ジェネレータを使用するエンティティを変更します(@ beforeまたは@after):

// reset Hibernate Sequences 
ResetableIdGenerator.cycle++; 

これは良い解決策ではありません - それはハックです。しかし、それはうまくいくかもしれません。

EDIT 20170504:最初の投稿に間違いがありました。パラメータ "sequenceName"と "allocationSize"はJPAパラメータです。 GenericGeneratorは休止状態からのものです。 "sequenceName"の代わりに "sequence"を使用する必要があります。 "allocationSize"の代わりに "max_lo"を使用してallocationSize-1に設定する必要があります。 コード例を更新しました。ごめんなさい!

+0

私が信じたくないことを確認するこの詳細な回答をありがとう。私は両方のint属性を単一のブール値をリセットし、あなたのハックで解決してください。あまりにも悲しいことに、これを行うためのクリーンな方法はありません... – GreenTurtle

0

myEntityManagerFactory.closeを();

myEntityManagerFactory = Persistence.createEntityManagerFactory(...);

+0

Thxしかし、あまりにも悲しいことに、このプロジェクトではJPAを使用していません。私の上記の質問の追加をご覧ください。 – GreenTurtle

1

おそらく、解決策の1つは、新しいセッションファクトリーで各JUNITテストを実行することです。これは、すべてのJUnitテストケースにより数秒かかる短所

  • あなたが最初
  • から

をシーケンスジェネレータを取得@Before@After

賛否

  • を使用してその開閉セッションファクトリ

更新別の方法は、私は同じ問題に直面していたし、それを行うためのinbuild方法を見つけることができませんでした

ALTER SEQUENCE Test.sequence RESTART WITH 1 
+0

新しいセッションファクトで各テストを実行すると、時間がかかりすぎて実行できなくなる(コンテキストリロードはこのプロジェクトで約15秒かかり、多くの統合テストがあります) – GreenTurtle

+0

@GreenTurtle can JUNITがinmemoryを使用しているので、私の更新された回答を試してみてください。 – rajadilipkolli

+0

この文は、Oracleでは(おそらくはH2ではなく)動作しません。しかし、私は影響を与えずにシーケンスを落として再作成しようとしました。私はcreateSQLQueryを使用していますが、Hibernateはこのようにシーケンスの可能性を気付かないのです... :-( – GreenTurtle

関連する問題