2016-09-02 6 views
3

情報が先頭に表示されます。私はちょうど私が必要と思うコードスニペットを選択しました、彼らは別のファイルに存在するので、少し混乱しているように見えるのではないかと思いません。選択中のスプリングバッチ+休止状態の書き込み

ジョブのSpringBatch Readerでフラットファイルから読んでいます。 カラムをHibernateモデルにマップするFieldSetMapperから呼び出されたProductValueMapperを書きました。このマッパーは、製品がデータベースにすでに存在するかどうかをチェックし、存在する場合はデータベースのエンティティを使用します。そうでない場合、新しい製品が作成されます。

@Component 
@StepScope 
public class ProductValueMapper { 

    @Autowired 
    private IProductDao productDao; 

    @Autowired 
    private IFactory<Product> productFactory; 

    private Product fetch(String[] criteria) { 
     //... try to fetch product using different criteria, or create a new one using the factory ... 
     return product; 
    } 

    Product map(String[] criteria) { 
      Product product = fetch(criteria); 
      //... map some stuff ...  
      return product; 
    } 

}

DAOのエンティティマネージャが

@PersistenceContext 
private EntityManager manager; 

によってAutowired取得し、その後@Transactional

としてマークされている私は、ロギングnothingexceptないプロセッサを持っています。

それから私はこのように作成されると、デフォルトjpaItemWriterに書き込む:> 1のチャンクと、私は問題を取得するバッチ中に変更されます項目とする以外は期待通りのプログラムが動作する

@Configuration 
@Import(DatabaseConfiguration.class) 
public class HibernateConfiguration extends DefaultBatchConfigurer { 

    @Autowired 
    @Qualifier("oracleDataSource") 
    private DataSource dataSource; 

    @Bean(name = "jpaEntitiyManager") 
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() { 
     LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); 
     em.setPersistenceUnitName("hibernatePersistenceUnit"); 
     em.setPackagesToScan("com.somepackage"); 
     em.setDataSource(dataSource); 
     em.setJpaProperties(hibernateProperties()); 

     HibernateJpaVendorAdapter vendor = new HibernateJpaVendorAdapter(); 
     vendor.setGenerateDdl(false); 
     vendor.setShowSql(true); 
     em.setJpaVendorAdapter(vendor); 
     return em; 
    } 

    @Bean 
    public Properties hibernateProperties() { 
     Properties prop = new Properties(); 
     prop.setProperty("hibernate.hbm2ddl.auto", "validate"); 
     prop.setProperty("hibernate.dialect", "org.hibernate.dialect.Oracle10gDialect"); 
     prop.setProperty("hibernate.globally_quoted_identifiers", "false"); 
     prop.setProperty("hibernate.show_sql", "true"); 
     return prop; 
    } 

    @Override 
    public PlatformTransactionManager getTransactionManager() { 
     final JpaTransactionManager transactionManager = new JpaTransactionManager(); 
     transactionManager.setEntityManagerFactory(entityManagerFactory().getObject()); 
    return transactionManager; 
    } 
} 

@Configuration 
@EnableBatchProcessing(modular = true) 
@ComponentScan({"com.somepackage"}) 
@Import({HibernateConfiguration.class, DatabaseConfiguration.class}) 
public class BatchConfiguration { 

    @Autowired 
    public EntityManagerFactory emf; 

    @Bean 
    public JpaItemWriter<ProductEntity> jpaItemWriter() { 
     JpaItemWriter<ProductEntity> itemWriter = new JpaItemWriter<>(); 
     itemWriter.setEntityManagerFactory(emf); 
     return itemWriter; 
    } 

    //... rest of the setup for the job 

} 

次の項目を選択している間は、hibernateはupdate文を実行します。

私はこれを解決するには、flushを呼び出してプロセッサに保存するか、チャンクサイズを1に減らすことができますが、どうにかして両方の解決策が間違っていると感じています。 アイテムごとにトランザクションを開いたままにしてはいけません。そして、ライターに電話をかけたら、これらのトランザクションは1つずつコミットされるべきですか?または、Spring Batchでトランザクションの処理の原則を誤解していますか?

* EDIT 1 *

問題は1にチャンクサイズを設定すると、期待どおりプログラムが動作することである:更新は、書き込みフェーズ中に発生します。

2016-09-05 11:20:40.828 INFO 11084 --- [   main] n.e.p.i.logging.LogItemReadListener  : ItemReadListener - beforeRead 
2016-09-05 11:20:40.828 INFO 11084 --- [   main] n.e.p.i.r.map.GenericProductMapper : Processing product: Prduct1 
Hibernate: select productent0_.PRODUCTSN as PRODUCTSN1_25_, ..... 
2016-09-05 11:20:40.832 INFO 11084 --- [   main] n.e.p.i.logging.LogItemReadListener  : ItemReadListener - afterRead: [email protected] 
2016-09-05 11:20:40.832 INFO 11084 --- [   main] n.e.p.i.logging.LogItemWriterListener : ItemWriteListener - beforeWrite 
Hibernate: update PIME.PRODUCT set AVAILABILITYDATE=?, .... 
2016-09-05 11:20:40.836 INFO 11084 --- [   main] n.e.p.i.logging.LogItemWriterListener : ItemWriteListener - afterWrite 
2016-09-05 11:20:40.887 INFO 11084 --- [   main] n.e.p.i.logging.LogItemReadListener  : ItemReadListener - beforeRead 
2016-09-05 11:20:40.887 INFO 11084 --- [   main] n.e.p.i.r.map.GenericProductMapper : Processing product: Product2 
Hibernate: select productent0_.PRODUCTSN as PRODUCTSN1_25_, .... 
2016-09-05 11:20:40.891 INFO 11084 --- [   main] n.e.p.i.logging.LogItemReadListener  : ItemReadListener - afterRead: c[email protected] 
2016-09-05 11:20:40.891 INFO 11084 --- [   main] n.e.p.i.logging.LogItemWriterListener : ItemWriteListener - beforeWrite 
2016-09-05 11:20:40.891 INFO 11084 --- [   main] n.e.p.i.logging.LogItemWriterListener : ItemWriteListener - afterWrite 

しかし、チャンクサイズを大きくした場合、書き込みは、製品の処理の最後ではなくチャンクで発生しませんので、書き込みは、select文のforntで起こる:

2016-09-05 11:09:36.240 INFO 12408 --- [   main] n.e.p.i.logging.LogItemReadListener  : ItemReadListener - beforeRead 
2016-09-05 11:09:36.240 INFO 12408 --- [   main] n.e.p.i.r.map.GenericProductMapper : Processing product: Product1 
Hibernate: select productent0_.PRODUCTSN as PRODUCTSN1_25_, .... 
2016-09-05 11:09:36.244 INFO 12408 --- [   main] n.e.p.i.logging.LogItemReadListener  : ItemReadListener - afterRead: [email protected] 
2016-09-05 11:09:36.244 INFO 12408 --- [   main] n.e.p.i.logging.LogItemReadListener  : ItemReadListener - beforeRead 
2016-09-05 11:09:36.244 INFO 12408 --- [   main] n.e.p.i.r.map.GenericProductMapper : Processing product: Product2 
Hibernate: update PIME.PRODUCT set AVAILABILITYDATE=?, .... 
Hibernate: select productent0_.PRODUCTSN as PRODUCTSN1_25_, .... 
2016-09-05 11:09:36.250 INFO 12408 --- [   main] n.e.p.i.logging.LogItemReadListener  : ItemReadListener - afterRead: [email protected] 
2016-09-05 11:09:36.250 INFO 12408 --- [   main] n.e.p.i.logging.LogItemReadListener  : ItemReadListener - beforeRead 
2016-09-05 11:09:36.250 INFO 12408 --- [   main] n.e.p.i.r.map.GenericProductMapper : Processing product: Product3 
Hibernate: select productent0_.PRODUCTSN as PRODUCTSN1_25_, .... 
2016-09-05 11:09:36.253 INFO 12408 --- [   main] n.e.p.i.logging.LogItemReadListener  : ItemReadListener - afterRead: [email protected] 
2016-09-05 11:09:36.253 INFO 12408 --- [   main] n.e.p.i.logging.LogItemReadListener  : ItemReadListener - beforeRead 
2016-09-05 11:09:36.253 INFO 12408 --- [   main] n.e.p.i.r.map.GenericProductMapper : Processing product: Product4 
Hibernate: select productent0_.PRODUCTSN as PRODUCTSN1_25_, .... 
2016-09-05 11:09:36.256 INFO 12408 --- [   main] n.e.p.i.logging.LogItemReadListener  : ItemReadListener - afterRead: [email protected] 
2016-09-05 11:09:36.256 INFO 12408 --- [   main] n.e.p.i.logging.LogItemWriterListener : ItemWriteListener - beforeWrite 
2016-09-05 11:09:36.257 INFO 12408 --- [   main] n.e.p.i.logging.LogItemWriterListener : ItemWriteListener - afterWrite 
+0

あなたはだけでなく、私にフェッチ()メソッドを示すことができました作家? –

+0

本質的にfetchメソッドは、IDおよび異なる他の基準で製品を検索するためにDaoを使用します。例えばproductDao.getById(criteria [0])。それが空のままであれば、最終的にnew Product()を返すことになるファクトリからcreate()を呼び出します。完全なメソッドはいくつかの制約をチェックし、異なる条件に基づいてfindByを実行しますが、それを掲示したくなかった。ライターは、最後のコードエントリの最後にあります。これは単にデフォルトのJpaItemWriterです。 – Xtroce

+0

さて、「バッチ中に変更されたアイテムは、次のアイテムを選択しているときに、Hibernateが更新ステートメントを実行するという問題が発生します。 ?私は理解の一種ですが、私はかなりではありません。問題を正確に明確にしてください(スタックトレースと例外は必要ならば) –

答えて

2

我々 Entityの代わりにEntryを使用する必要があります。この場合、あなたのためのベストプラクティスは、唯一のリーダーから

  1. である、あなたはデータベースからの照会およびエントリ(POJO)ではないエンティティとして保存します。
  2. プロセッサからは、エントリの処理(変更)
  3. Writerから、EntryからIDでデータベースを更新します。

そうでない場合は、春には以下のようなものを実行します(または、あなたがPOJOへのエンティティからのマッピングのためのドーザーを使用することができます):

  1. リーダー、あなたはAを取得し、アクティブなエンティティA.として保存します
  2. プロセッサー、直接エンティティ上で変更する
  3. もう1人の読者がBをフェッチしてから、Aエンティティで直接変更が失われるため、SpringはAを更新します。

注:あなたはそれが起こったくない場合、あなたは@Transactional

と一緒に@ReadOnly使用することができますおかげで、 Nghia

+0

これは良い解決策に見えますが、それに対して2つの質問です。この製品は、それにリンクされたかなりのデータから構成されています。これは、休止状態でLAZYをフェッチすることによって行われます。これはDozerでコピーするときに問題になる可能性があります。また、どこにReadOnlyアノテーションをTransactionalアノテーションに追加すれば、最後にSpringBatch経由でオブジェクトを書き込む可能性はないでしょうか? – Xtroce

+0

Session.setReadOnly([entity object]、true | false)を試して、readonlyを有効/無効にすることができます* –