简体   繁体   中英

How to manage shorter transaction inside Spring Batch chunk?

I've got a problem using Spring Batch and can't seem to find a solution.

So, I've got a batch processing some items in chunks (size 10). I've also got a transaction manager used by this batch to persist the processed items after each chunk.

But... I would also like to persist some progress status for those items, in real time. So before processing an item I want to save a status saying this item is in progress.

And I can't seem to find a solution to achieve that. I tried the following solutions :

  1. If I just annotate my status manager service with Transactional annotation the statuses are commited after the whole chunk processing.
  2. If I add REQUIRES_NEW as propagation level to the annotation, it works... but the batch ends in some kind of deadlock (I read that it was common issue with REQUIRES_NEW ).
  3. So my last guess was to add a second transaction manager (on the same datasource) and use it on the status manager service... But I got the same result as solution #1 (which seem wierd to me as I expected the transaction from this manager to act independently of the chunk transaction).

Has anybody ever encountered this problem?

EDIT : Here is my configuration, simplified on purpose :

Class DbConfiguration:

@Configuration
public class DbConfiguration {
    @Bean
    @Primary
    public JpaTransactionManager transactionManager() throws Exception {
        final JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
        jpaTransactionManager.setEntityManagerFactory(entityManagerFactory());
        jpaTransactionManager.afterPropertiesSet();
        return jpaTransactionManager;
    }
}

Class JobConfiguration:

@Configuration
@Import(DbConfiguration.class)
@EnableTransactionManagement
public class JobConfiguration {
    @Autowired
    private EntityManagerFactory entityManagerFactory;

    @Bean
    public Job jobDefinition() {
        return jobBuilderFactory
            .get(JOB_NAME)
            .start(step())
            .build();
    }

    @Bean
    public Step step() {
        return stepBuilderFactory
            .get(STEP_NAME)
            .<Object, Object>chunk(COMMIT_INTERVAL)
                    .reader(reader())
                    .processor(processor())
                    .writer(writer())
            .build();
    }

    @Bean
    public PlatformTransactionManager statusTransactionManager() {
        final JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
        jpaTransactionManager.setEntityManagerFactory(entityManagerFactory);
        jpaTransactionManager.afterPropertiesSet();
        return jpaTransactionManager;
    }
}

Class StatusManagerServiceImpl:

@Transactional("statusTransactionManager")
public class StatusManagerServiceImpl implements StatusManagerService {
    ...
}

A way to achieve this is to use Spring TransactionTemplate as follows:

@Service
public class StatusManagerServiceImpl implements StatusManagerService {
    @Autowired
    private PlatformTransactionManager transactionManager;

    public separateTransactionMethod() {
        TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                // Do something here that will be committed right away.
            }
        });
    }
}

You can alternatively use new TransactionCallback if you got a result to return. Then execute method would return that result.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM