I've got a toy project that imports a list of names as people and monsters to the database, then using then changes the last name of the monsters to Monster. To write items to the database I'm using the HibernateItemWriter
Each of the 3 jobs has its own configuration file and they are bundled in a single configuration with @EnableBatchProcessing(modular=true) annotation.
This all works.
However when the the @EnableBatchProcessing is present on the configuration files of the individual jobs the HibernateItemWriter can no longer find a hibernate session.
Can someone explain the application context scoping rules that make this happen?
It is rather nice to have the @EnableBatchProcessing on individual Job configurations when not using them individually. It allows them to be imported and ran while spring auto configuration does the work.
The application
@Import({ImportAllConfiguration.class, EmbededDatasourceConfiguration.class, HibernateConfiguration.class})
@Configuration
public class Application {
public static void main(String[] args) throws SQLException, BeansException, JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException, NoSuchJobException, JobParametersInvalidException {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
JobRegistry jobRegistry = ctx.getBean(JobRegistry.class);
Job job1 = jobRegistry.getJob("importMonsterJob");
JobLauncher launcher = ctx.getBean(JobLauncher.class);
launcher.run(job1, new JobParameters());
}
}
The ImportAllConfiguration
@Configuration
@EnableBatchProcessing(modular=true)
public class ImportAllConfiguration {
@Bean
public ApplicationContextFactory someJobs() {
return new GenericApplicationContextFactory(ImportMonsterConfiguration.class);
}
@Bean
public ApplicationContextFactory someMoreJobs() {
return new GenericApplicationContextFactory(ImportPersonConfiguration.class);
}
@Bean
public ApplicationContextFactory evenMoreJobs() {
return new GenericApplicationContextFactory(MatchMonsterToPersonConfiguration.class);
}
}
The Hibernate Config
@Configuration
public class HibernateConfiguration {
@Autowired
private DataSource dataSource;
@Bean
public HibernateTransactionManager transactionManager(
SessionFactory sessionFactory) {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(sessionFactory);
return txManager;
}
@Bean
public JdbcTemplate jdbcTemplate() {
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
@SuppressWarnings("serial")
public Properties hibernateProperties() {
return new Properties() {
{
//setProperty(Environment.CURRENT_SESSION_CONTEXT_CLASS, "thread");
setProperty(Environment.HBM2DDL_AUTO, "create");
setProperty(Environment.SHOW_SQL, "true");
}
};
}
@Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean annotationSessionFactoryBean = new LocalSessionFactoryBean();
annotationSessionFactoryBean.setPackagesToScan("hello.model");
annotationSessionFactoryBean.setDataSource(dataSource);
annotationSessionFactoryBean
.setHibernateProperties(hibernateProperties());
return annotationSessionFactoryBean;
}
}
A job configuration file. Adding @EnableBatchProcessing here will cause the HibernateItemWriter be unable to find a session.
@Configuration
@EnableBatchProcessing <-- This causes the exception
public class ImportMonsterConfiguration {
@Autowired
protected SessionFactory sessionFactory;
@Autowired
protected StepBuilderFactory stepBuilderFactory;
@Autowired
protected JobBuilderFactory jobBuilderFactory;
@Bean
public <T> ItemWriter<T> writer() {
return new HibernateItemWriter<T>() {
{
setSessionFactory(sessionFactory);
}
};
}
@Bean
public ItemReader<Monster> reader() {
FlatFileItemReader<Monster> reader = new FlatFileItemReader<Monster>();
reader.setResource(new ClassPathResource("sample-data.csv"));
reader.setLineMapper(new DefaultLineMapper<Monster>() {
{
setLineTokenizer(new DelimitedLineTokenizer() {
{
setNames(new String[] { "firstName", "lastName" });
}
});
setFieldSetMapper(new BeanWrapperFieldSetMapper<Monster>() {
{
setTargetType(Monster.class);
}
});
}
});
return reader;
}
@Bean
public ItemProcessor<Monster, Monster> processor() {
return new MonsterItemProcessor();
}
@Bean
public Job importMonsterJob() {
return jobBuilderFactory.get("importMonsterJob")
.incrementer(new RunIdIncrementer()).flow(step2()).end()
.build();
}
@Bean
public LocalValidatorFactoryBean validator() {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
return validator;
}
@Bean
public ItemProcessor<Monster, Monster> validatingProcessor() {
SpringValidator<Monster> springValidator = new SpringValidator<Monster>();
springValidator.setValidator(validator());
ValidatingItemProcessor<Monster> validatingItemProcessor = new ValidatingItemProcessor<Monster>();
validatingItemProcessor.setValidator(springValidator);
return validatingItemProcessor;
}
@Bean
public Step step2() {
return stepBuilderFactory.get("step2").<Monster, Monster> chunk(10)
.reader(reader()).processor(processor())
.processor(validatingProcessor()).writer(writer()).build();
}
}
The exception thrown.
org.hibernate.HibernateException: No Session found for current thread
at org.springframework.orm.hibernate4.SpringSessionContext.currentSession(SpringSessionContext.java:106)
at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:1014)
at org.springframework.batch.item.database.HibernateItemWriter.doWrite(HibernateItemWriter.java:134)
at org.springframework.batch.item.database.HibernateItemWriter.write(HibernateItemWriter.java:113)
You're providing your own transaction manager. To override the ones that Spring Batch provides with @EnableBatchProcessing, you need to provide your own BatchConfigurer implementation. Otherwise, we're going to add one for you and in this case, it's not going to be the Hibernate based transaction manager. Have HibernateConfiguration extend DefaultBatchConfigurer and override the getTransactionManager method with your transaction manager code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With