Comment partager les données entre les différentes étapes d'un travail dans le Lot de printemps?
en creusant dans le Lot de printemps, j'aimerais savoir comment partager les données entre les différentes étapes d'un travail?
peut-on utiliser JobRepository pour ça? Si oui, comment pouvons-nous faire?
y a-t-il une autre façon de faire ou de réaliser la même chose?
9 réponses
le référentiel d'emplois est utilisé indirectement pour transmettre des données entre les étapes (Jean-Philippe a raison de dire que la meilleure façon de le faire est de mettre des données dans le StepExecutionContext
et ensuite utiliser le nom verbeux ExecutionContextPromotionListener
pour promouvoir les clés de contexte d'exécution des pas vers le JobExecutionContext
.
il est utile de noter qu'il y a un auditeur pour promouvoir JobParameter
touches à un StepExecutionContext
aussi bien (encore plus verbeusement nommé JobParameterExecutionContextCopyListener
); vous trouverez ces beaucoup si des étapes de travail ne sont pas complètement indépendants l'un de l'autre.
sinon vous êtes laissé passer des données entre les étapes en utilisant des schémas encore plus élaborés, comme des files D'attente JMS ou (Dieu nous en préserve) des emplacements de fichiers codés.
pour ce qui est de la taille des données qui est transmise dans le contexte, je suggérerais aussi que vous la gardiez petite (mais je n'ai pas de détails sur le
à Partir d'une étape, vous pouvez mettre les données dans le StepExecutionContext
.
Puis, avec un écouteur, vous pouvez promouvoir des données de StepExecutionContext
JobExecutionContext
.
JobExecutionContext
est disponible dans toutes les étapes suivantes.
Attention : les données doivent être court.
Ces contextes sont sauvegardés dans le JobRepository
par sérialisation et la longueur est limitée (2500 caractères si je me souviens bien).
donc ces contextes sont bons pour partager des chaînes ou des valeurs simples, mais pas pour partager des collections ou des quantités énormes de données.
Partage d'énormes quantités de données n'est pas la philosophie de Spring Batch. Spring Batch est un ensemble d'actions distinctes, pas une énorme Unité de traitement D'entreprise.
je dirais que vous avez 3 options:
- Utiliser
StepContext
et de le promouvoir àJobContext
et vous y avez accès à partir de chaque étape, vous devez comme indiqué obéir à la limite de la taille - Créer
@JobScope
haricots et ajouter des données à cet haricot,@Autowire
il là où c'est nécessaire et l'utiliser (l'inconvénient est qu'il est dans la structure de mémoire et si le travail échoue les données sont perdues, peuvent causer des problèmes avec la restartabilité) - nous avions des ensembles de données plus importants à traiter par étapes (lire chaque ligne en csv et write to DB, read from DB, aggregate and send to API) nous avons donc décidé de modéliser les données dans une nouvelle table dans la même DB que les tables de méta-lots de printemps, garder
ids
JobContext
et accéder au besoin et supprimer cette table temporaire lorsque le travail se termine avec succès.
vous pouvez utiliser un objet Java Bean
- Exécuter une étape
- Stocker le résultat dans l'objet Java
- la prochaine étape référera le même objet java pour obtenir le résultat stocké par l'étape 1
De cette façon, vous pouvez stocker une grande collection de données si vous le souhaitez
Voici ce que j'ai fait pour enregistrer un objet qui est accessible à travers les étapes.
- créé un listener pour mettre l'objet en contexte de travail
@Component("myJobListener")
public class MyJobListener implements JobExecutionListener {
public void beforeJob(JobExecution jobExecution) {
String myValue = someService.getValue();
jobExecution.getExecutionContext().putString("MY_VALUE", myValue);
}
}
- Défini l'auditeur dans le contexte du poste
<listeners>
<listener ref="myJobListener"/>
</listeners>
- Consommé la valeur de l'étape à l'aide de BeforeStep annotation
@BeforeStep
public void initializeValues(StepExecution stepExecution) {
String value = stepExecution.getJobExecution().getExecutionContext().getString("MY_VALUE");
}
Vous pouvez stocker des données dans l'objet simple. Comme:
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")
.<Some, Any> chunk(someInteger1)
.reader(itemReader1())
.processor(itemProcessor1())
.writer(itemWriter1(yourObject))
.build();
}
public Step step2() {
return stepBuilderFactory.get("step2Name")
.<Some, Any> chunk(someInteger2)
.reader(itemReader2())
.processor(itemProcessor2(yourObject))
.writer(itemWriter2())
.build();
}
il suffit d'ajouter des données pour objecter dans l'écrivain ou toute autre méthode et obtenir dans n'importe quelle étape de la prochaine étape
on m'a donné une tâche d'invoquer le travail par lot un par un.Chaque tâche dépend d'un autre. Le premier résultat de travail doit exécuter le programme de travail conséquent. Je cherchais comment transmettre les données après l'exécution du travail. J'ai trouvé que ce programme de promotion de L'Exécutioncontextpromotionlistener est pratique.
1) j'ai ajouté un haricot pour "ExecutionContextPromotionListener" comme ci-dessous
@Bean
public ExecutionContextPromotionListener promotionListener()
{
ExecutionContextPromotionListener listener = new ExecutionContextPromotionListener();
listener.setKeys( new String[] { "entityRef" } );
return listener;
}
2) alors j'ai attaché un de l'auditeur à mes pas
Step step = builder.faultTolerant()
.skipPolicy( policy )
.listener( writer )
.listener( promotionListener() )
.listener( skiplistener )
.stream( skiplistener )
.build();
3) j'ai ajouté stepExecution comme référence dans l'implémentation step de mon rédacteur et peuplé dans le Beforestep
@BeforeStep
public void saveStepExecution( StepExecution stepExecution )
{
this.stepExecution = stepExecution;
}
4) à la fin de mon étape d'écriture, j'ai rempli les valeurs dans la version stepexécution comme les clés comme ci-dessous
lStepContext.put( "entityRef", lMap );
5) Après l'exécution de la tâche, j'ai récupéré les valeurs
lExecution.getExecutionContext()
et rempli comme réponse d'emploi.
6) de l'objet de réponse, je vais obtenir les valeurs et remplir les valeurs requises dans le reste de l' Jobs.
le code ci-dessus est pour promouvoir les données des étapes vers ExecutionContext en utilisant ExecutionContextPromotionListener. On peut le faire dans l'une quelconque des étapes.
Utiliser ' ExecutionContextPromotionListener .
public class YourItemWriter implements ItemWriter<Object> {
private StepExecution stepExecution;
public void write(List<? extends Object> 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;
}
}
Maintenant, vous devez ajouter promotionListener à votre travail
@Bean
public Step step1() {
return stepBuilder
.get("step1")<Company,Company> chunk(10)
.reader(reader()).processor(processor()).writer(writer())
.listener(promotionListner()).build();
}
@Bean public ExecutionContextPromotionListener promotionListner() { ExecutionContextPromotionListener listner = new ExecutionContextPromotionListener(); listner.setKeys (new String [] {"someKey"}); listner.setStrict (true); retour listner; }
maintenant, à l'étape 2 Obtenez vos données de job Exécutioncontext
public class RetrievingItemWriter implements ItemWriter<Object> {
private Object someObject;
public void write(List<? extends Object> items) throws Exception {
// ...
}
@BeforeStep
public void retrieveInterstepData(StepExecution stepExecution) {
JobExecution jobExecution = stepExecution.getJobExecution();
ExecutionContext jobContext = jobExecution.getExecutionContext();
this.someObject = jobContext.get("someKey");
}
}
si vous travaillez avec des tasklets, utilisez follwing pour obtenir ou mettre ExecutionContext
List<YourObject> yourObjects = (List<YourObject>) chunkContent.getStepContext().getJobExecutionContext().get("someKey");
comme Nenad Bozic l'a dit dans sa 3ème option, Utilisez les tables temp pour partager les données entre les étapes, en utilisant le contexte pour partager fait aussi la même chose, il écrit à la table et se charge à l'étape suivante, mais si vous écrivez dans les tables temp vous pouvez nettoyer à la fin de la tâche.