Spring Batching Batch Modulation Rules

I have a toy project that imports a list of names as people and monsters into a database, and then uses the then Monster name changes. To write items to the database I am using HibernateItemWriter

Each of the three jobs has its own configuration file, and they are combined into one configuration with the @EnableBatchProcessing annotation (modular = true).

It all works.

However, when @EnableBatchProcessing is present in the individual job configuration files, the HibernateItemWriter can no longer find the hibernate session.

Can someone explain the rules for determining the application context to make this happen?

It's pretty nice to have @EnableBatchProcessing in individual Job configurations when you're not using them separately. It allows them to be imported and run while spring does auto-configuration.

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());

    }

}

      

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);
    }
}

      

Hibernate configuration

@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;
    }

}

      

Job configuration file. Adding @EnableBatchProcessing here will result in the HibernateItemWriter not being able to find the 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();
    }

}

      

An exception.

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)

      

+3


source to share


1 answer


You provide your own transaction manager. To override the ones that Spring Batch provides with @EnableBatchProcessing

you need to provide your own implementation BatchConfigurer

. Otherwise, we'll add it for you, in which case it won't be a Hibernate based transaction manager. HibernateConfiguration

extend DefaultBatchConfigurer

and override the method getTransactionManager

with your transaction manager code.



+1


source







All Articles