配置自定义 HibernateItemWriter 时获取“必须提供 HibernateOperations 或 SessionFactory”
Posted
技术标签:
【中文标题】配置自定义 HibernateItemWriter 时获取“必须提供 HibernateOperations 或 SessionFactory”【英文标题】:Getting "Either HibernateOperations or SessionFactory must be provided" when configuring custom HibernateItemWriter 【发布时间】:2017-09-13 18:22:58 【问题描述】:所以我正在尝试构建一个使用 Hibernate 写入 mysql 数据库的应用程序,并且有一段时间试图让它工作。简而言之,它没有看到会话,即使我已经配置并自动装配了一个。这是我的代码明智的。
首先是该应用的配置:
@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
【问题讨论】:
作者的SessionFactory
设置在哪里?
你能显示完整的堆栈跟踪/错误吗?你如何设置春季批次?我注意到在您的LCBOStoreWriter.write()
中,您将 Session 转换为 SessionFactory。那是行不通的!尝试使用 SessionFactory sessionFactory = entityManagerFactory.unwrap(SessionFactory.class);
设置 HibernateItemWriter 的 SessionFactory
HibernateItemWriter
需要在afterPropertiesSet
方法中检查的明确连接的sessionFactory
。另外恕我直言,您的应用程序存在缺陷,因为您不需要SessionFactory
和EntityMangerFactory
。你为什么还要使用HibernateItemWriter
?您应该改用JpaItemWriter
,因为您不需要现在的破解/解决方法。
@MichaelMinella 我在编写器中使用 this.setSessionFactory(databaseConfig.getSessionFactory()) 设置 SessionFactory 的地方无效。
只需使用一个普通的JpaItemWriter
你不需要扩展它,这是你的全部问题,你的扩展正在破坏作者的正常工作。
【参考方案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();
【讨论】:
所以自动装配 dbConfig,修改 sessionFactory 以适应您的示例,修改 Step 配置并将 lCBOStoreWriter 添加到 Batch Config,但遗憾的是仍然是完全相同的错误。但是,尝试了以下方法:wr.setSessionFactory(dbConfig.entityManagerFactory().unwrap(SessionFactory.class));并将错误替换为“包含在没有活动事务的情况下无效”错误 你能调试 lCBOStoreWriter() 和 getLCBOStoreDataStep() 并验证 sessionfactory 设置为 not 吗?实际上,我同意@m-deinum。为什么要扩展 HibernateItemWriter?您可能应该将其设置为带有会话工厂的 bean。处理器应该简单地返回休眠实体,编写器应该自动编写它们,而不需要服务。 您看过本指南吗? spring.io/guides/gs/batch-processing 这是一个很好的说明点,您可以将其扩展为与 hibernate 一起使用。在这里,您将使用 application.properties 设置数据源,而不是在 config 中设置它。 Spring Boot 将为您提供数据源。然后我认为你可以自动装配 sessionfactory / entityManager 告诉它是怎么回事:) 我找到了各种各样的解决方案。我已经在下面发布了我的答案。【参考方案2】:因此,从我收到的反馈来看,正如我所担心的那样,我创建了一个 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 创建的内容,然后在您的 Step 配置中为您的编写器添加该方法。
然后是 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 在这方面的帮助。非常感谢!
【讨论】:
这仍然是一个弗兰肯斯坦解决方案,您不需要那个包装器......它除了复杂性之外没有增加任何东西。这也让我担心你的其他读者和处理器。 我认为你设法让它运行真是太好了!第一次让 bean 运行起来可能是一个挑战。正如@M.Deinum 建议的那样,尝试在不扩展休眠编写器的情况下使其工作,您将开始拥有spring batch ;) @M.Deinum 我相信我了解您来自哪里,并且已经为答案添加了一个附录,除非您知道您需要扩展您的编写器/处理器(公关很愚蠢并且就像那个样式一样),只需将其放在您配置步骤的配置样式中即可。 如果你扩展/包装东西,至少知道你在做什么。有些编写器实现了更多接口,而您的包装方式(仅使用ItemWriter
)可能会破坏这些编写器(或其他组件)的正常运行。因此,我强烈建议不要在没有正确理解 Spring Batch 的详细信息的情况下扩展默认值,因为通常会中断。以上是关于配置自定义 HibernateItemWriter 时获取“必须提供 HibernateOperations 或 SessionFactory”的主要内容,如果未能解决你的问题,请参考以下文章