在配置自定义HibernateItemWriter时

时间:2017-04-18 03:49:27

标签: java mysql hibernate spring-boot spring-batch

所以我正在尝试构建一个使用Hibernate写入MySQL数据库的应用程序,并且有一段时间试图让它工作。简而言之,它没有看到Session,即使我已经配置了一个和Autowired。这就是我的代码明智。

首先是所述应用程序的配置:

@EnableBatchProcessing
@SpringBootApplication(scanBasePackages="com.lcbo")
@EnableIntegration
public class BatchConfig {

@Autowired
private JobBuilderFactory jobBuilderFactory;

@Autowired
private StepBuilderFactory stepBuilderFactory;

@Bean
public Job processLCBOInventory(@Qualifier("getCurrentLCBODataStep") final Step getCurrentLCBODataStep,
                                @Qualifier("getLCBOStoreDataStep") final Step getLCBOStoreDataStep,
                                final JobExecutionListenerConfig jelcListener
                                ) {
    return jobBuilderFactory
            .get("processLCBOInventory")
            .incrementer(new RunIdIncrementer())
            .start(getCurrentLCBODataStep)
            .next(getLCBOStoreDataStep)
            .listener(jelcListener)
            .build();

}

@Bean
public Step getLCBOStoreDataStep(final LCBOStoreReader lcboStoreReader,
                                 final LCBOStoreProcessor lcboStoreProcessor,
                                 final LCBOStoreWriter lcboStoreWriter,
                                 final ExecutionContextPromotionListener listener) throws Exception {

    return stepBuilderFactory
            .get("getLCBOStoreDataStep")
            .<LCBOStore, LCBOStore>chunk(inventoryTrackerProperties.getDefaults().getChunkSize())
            .reader(lcboStoreReader.read())
            .processor(lcboStoreProcessor)
            .writer(lcboStoreWriter)
            .listener(listener)
            .build();
}

然后,作者:

@Component
public class LCBOStoreWriter extends HibernateItemWriter<LCBOStore> {

        @Autowired
private LCBOStoreService lcboStoreService;

@Autowired
private DatabaseConfig dbConfigy;

private static final Logger log = LoggerFactory.getLogger(LCBOStoreWriter.class);

@Override
public void write(List<? extends LCBOStore> lcboStoreItems) {

    for (LCBOStore lcboStoreItem : lcboStoreItems) {
        lcboStoreService.addNewStore(lcboStoreItem);
    }

}

和DatabaseConfig文件内容:

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = "com.lcbo.domain")
public class DatabaseConfig {

    @Autowired
    private LCBOInventoryTrackerProperties properties;

    @Bean
    public EntityManagerFactory entityManagerFactory() throws SQLException {

        final LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
        factoryBean.setDataSource(dataSource());
        factoryBean.setJpaDialect(new HibernateJpaDialect());
        factoryBean.setJpaProperties(getHibernateProperties());
        factoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
        factoryBean.setPackagesToScan("com.lcbo.domain");
        factoryBean.setPersistenceProviderClass(HibernatePersistenceProvider.class);
        factoryBean.setPersistenceUnitName("persistenceUnit");
        factoryBean.afterPropertiesSet();


        return factoryBean.getObject();
    }

    @Bean(destroyMethod = "")
    public DataSource dataSource() throws SQLException {
        BasicDataSource dataSource = new BasicDataSource();

        dataSource.setDriverClassName(properties.getDb().getDriver());
        dataSource.setUrl(properties.getDb().getUrl());
        dataSource.setUsername(properties.getDb().getUsername());
        dataSource.setPassword(properties.getDb().getPassword());

        return dataSource;
    }

甚至尝试过SessionFactory本身,就像这样

public LocalSessionFactoryBean getSessionFactory() throws SQLException {
    LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
    sessionFactory.setDataSource(dataSource());
    sessionFactory.setPackagesToScan(new String[] { "com.bytestree.model" });
    sessionFactory.setHibernateProperties(getHibernateProperties());

    return sessionFactory;
}

但没有骰子。我还尝试将一个SessionFactory实例注入到HibernateItemWriter附带的setSessionFactory中,再次,没有骰子。

我知道我遇到了一些问题,问题是它希望SessionFactory能够被应用程序重新配置,或者我是否将这个编写器配置为接近正确的位置?

编辑:根据要求,运行此代码时显示在intellj中的堆栈跟踪。

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'LCBOInventoryWriter' defined in file [C:\workspace\LCBOInventoryTracker\target\classes\com\lcbo\writer\LCBOInventoryWriter.class]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: Either HibernateOperations or SessionFactory must be provided
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1628) ~[spring-beans-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555) ~[spring-beans-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483) ~[spring-beans-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) ~[spring-beans-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) ~[spring-beans-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) ~[spring-beans-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) ~[spring-beans-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761) ~[spring-beans-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:866) ~[spring-context-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:542) ~[spring-context-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:737) [spring-boot-1.5.1.RELEASE.jar:1.5.1.RELEASE]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:370) [spring-boot-1.5.1.RELEASE.jar:1.5.1.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:314) [spring-boot-1.5.1.RELEASE.jar:1.5.1.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1162) [spring-boot-1.5.1.RELEASE.jar:1.5.1.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1151) [spring-boot-1.5.1.RELEASE.jar:1.5.1.RELEASE]
    at com.lcbo.config.LCBOBatchConfig.main(LCBOBatchConfig.java:157) [classes/:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_102]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_102]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_102]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_102]
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147) [idea_rt.jar:na]
Caused by: java.lang.IllegalStateException: Either HibernateOperations or SessionFactory must be provided
    at org.springframework.util.Assert.state(Assert.java:392) ~[spring-core-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.batch.item.database.HibernateItemWriter.afterPropertiesSet(HibernateItemWriter.java:93) ~[spring-batch-infrastructure-3.0.7.RELEASE.jar:3.0.7.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1687) ~[spring-beans-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1624) ~[spring-beans-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    ... 20 common frames omitted

2 个答案:

答案 0 :(得分:1)

由于HibernateItemWriter是第三方依赖项,我认为将LCBOStoreWriter设置为@Bean而不是@Component可能更好,并在那里设置sessionFactory

从LCBOStoreWriter中移除@Component并将以下内容添加到BatchConfig

@Bean
public LCBOStoreWriter lCBOStoreWriter() {
    LCBOStoreWriter wr = new LCBOStoreWriter();
    wr.setSessionFactory(sessionFactory())
    return wr;
}

如果没有自动装配,您可能需要设置lcboStoreService和dbConfigy。或者,你可以用其他方式注射它们,我不记得所有的方式......

@Bean
public SessionFactory sessionFactory() throws SQLException {
    LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
    sessionFactory.setDataSource(dataSource());
    sessionFactory.setPackagesToScan(new String[] { "com.bytestree.model" });
    sessionFactory.setHibernateProperties(getHibernateProperties());
    return sessionFactory.getObject();
}

答案 1 :(得分:0)

因此,根据我收到的反馈,听起来就像我担心的那样,我创建了一个frakenstein解决方案,从多个来源在线拼凑而成。通过大量的帮助和一些反复试验,我通过以下方式解决了这个问题。

首先,这是作家现在的样子。

@Component
public class LCBOStoreWriter implements ItemWriter<LCBOStore> {

    @Autowired
    private DatabaseConfig dbConfig;

    @Override
    public void write(List<? extends LCBOStore> items) throws Exception {

        JpaItemWriter wr = new JpaItemWriter();

        wr.setEntityManagerFactory(dbConfig.entityManagerFactory());
        wr.write(items);

    }
}
编辑:我扩展了作者主要是因为将功能分离到自己的类中。我发现随着项目的进行,这些事情就像扩展一样,所以一开始就把它们分开。另外我喜欢这个样子。话虽如此,它并不是必需的,而且是@M。 Deinum指出可以使事情变得更加复杂。除非你知道你需要使用自定义编写器扩展功能,或者像我一样愚蠢的事情,你发现分离看起来更好代码,请使用@qtips创建的内容,然后在步骤配置中为您添加方法作家。

然后是LCBOStore,它是java对象和Hibernate / JPA注释的组合。

@Entity
@Table(name = "store")
public class LCBOStore {

    @Id
    @Column(name = "id", unique = true, nullable = false)
    private Long id;

    @Column(name = "addressLineOne", length = 255)
    private String addressLineOne;

    @Column(name = "addressLineTwo", length = 255)
    private String addressLineTwo;

    @Column(name = "city", length = 255)
    private String city;

    @Column(name = "postalCode", length = 10)
    private String postalCode;

    @Column(name = "latitude", length = 255)
    private String latitude;

    @Column(name = "longitude", length = 255)
    private String longitude;

    @Column(name = "updatedAt", length = 255)
    private String updatedAt; //Convert to Date

//getters and setters

}

自动装配的entityManagerFactory类:

@Bean
public EntityManagerFactory entityManagerFactory() throws SQLException {

    final LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
    factoryBean.setDataSource(dataSource());
    factoryBean.setJpaDialect(new HibernateJpaDialect());
    factoryBean.setJpaProperties(getHibernateProperties());
    factoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
    factoryBean.setPackagesToScan("com.lcbo.batch.domain");
   factoryBean.setPersistenceProviderClass(HibernatePersistenceProvider.class);
    factoryBean.setPersistenceUnitName("persistenceUnit");
    factoryBean.afterPropertiesSet();


    return factoryBean.getObject();
}

以及步骤

的配置
@Bean
public Step getLCBOStoreDataStep(final LCBOStoreReader lcboStoreReader,
                                 final LCBOStoreProcessor lcboStoreProcessor,
                                 final LCBOStoreWriter lcboStoreWriter,
                                 final ExecutionContextPromotionListener listener) throws Exception {

    return stepBuilderFactory
            .get("getLCBOStoreDataStep")
            .<LCBOStore, LCBOStore>chunk(inventoryTrackerProperties.getDefaults().getChunkSize())
            .reader(lcboStoreReader.read())
            .processor(lcboStoreProcessor)
            .writer(lcboStoreWriter)
            .listener(listener)
            .build();
}

我要感谢qtips和M. Deinum对此的帮助。非常感谢!