选择期间Spring批处理+ hibernate写入

时间:2016-09-02 09:36:15

标签: java hibernate spring-batch

预先提供信息。我刚刚选择了我认为必要的代码片段,它们位于不同的文件中,因此不要怀疑它是否看起来有点令人困惑。

我在SpringBatch Reader工作期间从平面文件中读取数据。 我编写了一个从FieldSetMapper调用的ProductValueMapper,它将列映射到Hibernate模型。此映射器还检查产品是否已存在于数据库中,如果存在,则使用数据库中的实体,否则将创建新的实体。

@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通过

获得实体经理Autowired
@PersistenceContext
private EntityManager manager;

并标记为@Transactional

之后我有一个处理器,它不会记录日志。

然后我写入默认的jpaItemWriter,其创建方式如下:

@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

}

该程序按预期工作,除了使用&gt;的块大小。 1和批处理期间发生变化的项我得到了hibernate在选择以下项时执行更新语句的问题。

我知道我可以通过在处理器中调用flush和save或者将块大小减少到1来解决这个问题,但不知何故,这两种解决方案对我来说都是错误的。 不是每个项目都有一个交易保持打开,然后在调用作者时这些交易应该一个接一个地提交吗?或者我误解了Spring Batch中transactionHandling的原理。

*编辑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: com.somepackage.ProductEntity@8e654f7
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: com.somepackage.ProductEntity@2c7fb24c
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语句中,因为写入不是在处理产品结束时发生的,而是以块的形式发生:

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: com.somemodule.ProductEntity@6f28a07e
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: com.somemodule.ProductEntity@71852f76
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: com.somemodule.ProductEntity@76ac8c3d
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: com.somemodule.ProductEntity@6a0d47e8
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

1 个答案:

答案 0 :(得分:2)

我们需要使用Entry而不是Entity。在这种情况下,您的最佳做法是

  1. 从Reader中,您从Database查询并将其存储为Entry(Pojo)而不是Entity。
  2. 从处理器,您处理(更改)条目
  3. 从Writer中,您可以从Entry更新数据库ID。 (或者您可以使用Dozer从实体映射到Pojo)
  4. 否则,Spring将执行以下操作:

    1. Reader,您获取A并将其存储为活动实体A.
    2. 处理器,您直接在A实体上进行更改
    3. 另一位读者,你拿B,然后Spring将更新A,因为他们直接在A实体上瑕疵改变。
    4. 注意:如果您不希望它发生,您可以使用@ReadOnly和@Transactional

      谢谢, Nghia酒店