Como podemos compartilhar dados entre as diferentes etapas de um trabalho no Spring Batch?

Cavando em Spring Batch, gostaria de saber como podemos compartilhar dados entre as diferentes etapas de um trabalho?

Podemos usar o JobRepository para isso? Se sim, como podemos fazer isso?

Existe alguma outra maneira de fazer / alcançar o mesmo?

o repository de tarefas é usado indiretamente para passar dados entre etapas (Jean-Philippe está certo de que a melhor maneira de fazer isso é colocar dados no StepExecutionContext e usar o ExecutionContextPromotionListener para promover as chaves de contexto de execução da etapa ao JobExecutionContext .

É útil notar que há um ouvinte para promover JobParameter chaves JobParameter para um StepExecutionContext também (o StepExecutionContext , ainda mais JobParameterExecutionContextCopyListener chamado); você descobrirá que usa muito isso se as etapas do seu trabalho não forem completamente independentes umas das outras.

Caso contrário, você fica transmitindo dados entre etapas usando esquemas ainda mais elaborados, como filas JMS ou (céu proíbe) locais de arquivos codificados.

Quanto ao tamanho dos dados que são passados ​​no contexto, eu também sugiro que você os mantenha pequenos (mas eu não tenho nenhum detalhe

De uma etapa, você pode colocar dados no StepExecutionContext . Em seguida, com um ouvinte, você pode promover dados de StepExecutionContext para JobExecutionContext .

Este JobExecutionContext está disponível em todas as etapas a seguir.

Becareful: os dados devem ser curtos. Esses contextos são salvos no JobRepository por serialização e o comprimento é limitado (2.500 caracteres, se bem me lembro).

Portanto, esses contextos são bons para compartilhar strings ou valores simples, mas não para compartilhar collections ou grandes quantidades de dados.

Compartilhar grandes quantidades de dados não é a filosofia da Spring Batch. Spring Batch é um conjunto de ações distintas, não uma enorme unidade de processamento de negócios.

Eu diria que você tem 3 opções:

  1. Use StepContext e promova-o para JobContext e você tem access a ele de cada passo, você deve, como observado, obedecer ao limite de tamanho
  2. Crie o bean @JobScope e adicione dados a esse bean, @Autowire o @Autowire onde necessário e use-o (a desvantagem é que ele está na estrutura da memory e, se o job falhar, os dados serão perdidos, causando problemas de reboot)
  3. Tínhamos conjuntos de dados maiores necessários para serem processados ​​em etapas (ler cada linha no csv e gravar no database, ler do database, agregar e enviar para a API), por isso decidimos modelar dados em uma nova tabela no mesmo database. ids no JobContext e access quando necessário e exclua essa tabela temporária quando o trabalho for concluído com êxito.

Você pode usar um object Java Bean

  1. Execute um passo
  2. Armazene o resultado no object Java
  3. A próxima etapa indicará o mesmo object java para obter o resultado armazenado na etapa 1

Desta forma, você pode armazenar uma enorme coleção de dados se você quiser

Aqui está o que eu fiz para salvar um object que é acessível através dos passos.

  1. Criado um ouvinte para definir o object no contexto de trabalho
 @Component("myJobListener") public class MyJobListener implements JobExecutionListener { public void beforeJob(JobExecution jobExecution) { String myValue = someService.getValue(); jobExecution.getExecutionContext().putString("MY_VALUE", myValue); } } 
  1. Definiu o ouvinte no contexto do trabalho
    
  1. Consumido o valor na etapa usando a anotação BeforeStep
 @BeforeStep public void initializeValues(StepExecution stepExecution) { String value = stepExecution.getJobExecution().getExecutionContext().getString("MY_VALUE"); } 

Recebi uma tarefa para invocar o trabalho em lote, um por um. Cada trabalho depende de outro. O primeiro resultado do trabalho precisa executar o programa de trabalho resultante. Eu estava pesquisando como passar os dados após a execução do trabalho. Eu achei que este ExecutionContextPromotionListener vem a calhar.

1) Eu adicionei um bean para “ExecutionContextPromotionListener” como abaixo

 @Bean public ExecutionContextPromotionListener promotionListener() { ExecutionContextPromotionListener listener = new ExecutionContextPromotionListener(); listener.setKeys( new String[] { "entityRef" } ); return listener; } 

2) Então eu anexei um dos ouvintes aos meus passos

 Step step = builder.faultTolerant() .skipPolicy( policy ) .listener( writer ) .listener( promotionListener() ) .listener( skiplistener ) .stream( skiplistener ) .build(); 

3) Eu adicionei o stepExecution como referência na implementação da minha etapa do Writer e preenchi o Beforestep

 @BeforeStep public void saveStepExecution( StepExecution stepExecution ) { this.stepExecution = stepExecution; } 

4) no final da minha etapa de gravação, eu preenchi os valores na execução da etapa como as chaves abaixo

 lStepContext.put( "entityRef", lMap ); 

5) Após a execução do job, recuperei os valores de lExecution.getExecutionContext() e preenchi como resposta do job.

6) a partir do object de resposta do job, obterei os valores e preencherei os valores necessários no restante dos jobs.

O código acima é para promover os dados das etapas para ExecutionContext usando ExecutionContextPromotionListener. Isso pode ser feito em qualquer etapa.

Use `ExecutionContextPromotionListener.

 public class YourItemWriter implements ItemWriter { private StepExecution stepExecution; public void write(List items) throws Exception { // Some Business Logic // put your data into stepexecution context ExecutionContext stepContext = this.stepExecution.getExecutionContext(); stepContext.put("someKey", someObject); } @BeforeStep public void saveStepExecution(Final StepExecution stepExecution) { this.stepExecution = stepExecution; } } 

Agora você precisa adicionar o promotionListener ao seu trabalho

 @Bean public Step step1() { return stepBuilder .get("step1") chunk(10) .reader(reader()).processor(processor()).writer(writer()) .listener(promotionListner()).build(); } 

@Bean public ExecutionContextPromotionListener promotionListner () {ExecutionContextPromotion Listener = new ExecutionContextPromotionListener (); listner.setKeys (new String [] {“someKey”}); listner.setStrict (true); listener de retorno; }

Agora, na etapa 2, pegue seus dados do job ExecutionContext

 public class RetrievingItemWriter implements ItemWriter { private Object someObject; public void write(List items) throws Exception { // ... } @BeforeStep public void retrieveInterstepData(StepExecution stepExecution) { JobExecution jobExecution = stepExecution.getJobExecution(); ExecutionContext jobContext = jobExecution.getExecutionContext(); this.someObject = jobContext.get("someKey"); } } 

Se você estiver trabalhando com tasklets, use follwing para obter ou colocar ExecutionContext

 List yourObjects = (List) chunkContent.getStepContext().getJobExecutionContext().get("someKey"); 

Você pode armazenar dados no object simples. Gostar:

 AnyObject yourObject = new AnyObject(); public Job build(Step step1, Step step2) { return jobBuilderFactory.get("jobName") .incrementer(new RunIdIncrementer()) .start(step1) .next(step2) .build(); } public Step step1() { return stepBuilderFactory.get("step1Name") . chunk(someInteger1) .reader(itemReader1()) .processor(itemProcessor1()) .writer(itemWriter1(yourObject)) .build(); } public Step step2() { return stepBuilderFactory.get("step2Name") . chunk(someInteger2) .reader(itemReader2()) .processor(itemProcessor2(yourObject)) .writer(itemWriter2()) .build(); } 

Basta adicionar dados ao object no gravador ou qualquer outro método e obtê-lo em qualquer etapa da próxima etapa

Como Nenad Bozic disse em sua terceira opção, use as tabelas temporárias para compartilhar os dados entre as etapas, usando o contexto para compartilhar também faz a mesma coisa, escreve na tabela e carrega de volta na próxima etapa, mas se você escrever em tabelas temporárias você pode limpar o fim do trabalho.

    Intereting Posts