|
33 | 33 | import org.springframework.batch.core.step.builder.StepBuilder; |
34 | 34 | import org.springframework.batch.core.step.skip.NeverSkipItemSkipPolicy; |
35 | 35 | import org.springframework.batch.core.step.skip.NonSkippableProcessException; |
36 | | -import org.springframework.batch.infrastructure.item.ItemProcessor; |
37 | | -import org.springframework.batch.infrastructure.item.ItemReader; |
38 | | -import org.springframework.batch.infrastructure.item.ItemWriter; |
| 36 | +import org.springframework.batch.infrastructure.item.*; |
39 | 37 | import org.springframework.batch.infrastructure.item.support.ListItemReader; |
40 | 38 | import org.springframework.batch.infrastructure.item.support.ListItemWriter; |
41 | 39 | import org.springframework.batch.infrastructure.support.transaction.ResourcelessTransactionManager; |
|
52 | 50 | /** |
53 | 51 | * @author Mahmoud Ben Hassine |
54 | 52 | * @author Andrey Litvitski |
| 53 | + * @author xeounxzxu |
55 | 54 | */ |
56 | 55 | public class ChunkOrientedStepTests { |
57 | 56 |
|
@@ -319,4 +318,52 @@ class SkippableException extends RuntimeException { |
319 | 318 | assertEquals(1, stepExecution.getSkipCount()); |
320 | 319 | } |
321 | 320 |
|
| 321 | + @Test |
| 322 | + void testItemStreamUpdateStillOccursWhenChunkRollsBack_bugReproduction() throws Exception { |
| 323 | + // given: tracking stream to capture update invocations |
| 324 | + TrackingItemStream trackingItemStream = new TrackingItemStream(); |
| 325 | + ItemReader<String> reader = new ListItemReader<>(List.of("item1")); |
| 326 | + ItemWriter<String> writer = chunk -> { |
| 327 | + throw new RuntimeException("Simulated failure"); |
| 328 | + }; |
| 329 | + JobRepository jobRepository = new ResourcelessJobRepository(); |
| 330 | + ChunkOrientedStep<String, String> step = new ChunkOrientedStep<>("step", 1, reader, writer, jobRepository); |
| 331 | + step.registerItemStream(trackingItemStream); |
| 332 | + step.afterPropertiesSet(); |
| 333 | + JobInstance jobInstance = new JobInstance(1L, "job"); |
| 334 | + JobExecution jobExecution = new JobExecution(1L, jobInstance, new JobParameters()); |
| 335 | + StepExecution stepExecution = new StepExecution(1L, "step", jobExecution); |
| 336 | + |
| 337 | + // when: execute step (writer causes chunk rollback) |
| 338 | + step.execute(stepExecution); |
| 339 | + |
| 340 | + // then: due to current bug the stream update count becomes 1 although chunk |
| 341 | + // rolled back |
| 342 | + assertEquals(0, trackingItemStream.getUpdateCount(), |
| 343 | + "ItemStream should not be updated when chunk transaction fails (bug reproduction)"); |
| 344 | + } |
| 345 | + |
| 346 | + private static final class TrackingItemStream implements ItemStream { |
| 347 | + |
| 348 | + private int updateCount; |
| 349 | + |
| 350 | + @Override |
| 351 | + public void open(ExecutionContext executionContext) { |
| 352 | + } |
| 353 | + |
| 354 | + @Override |
| 355 | + public void update(ExecutionContext executionContext) { |
| 356 | + this.updateCount++; |
| 357 | + } |
| 358 | + |
| 359 | + @Override |
| 360 | + public void close() { |
| 361 | + } |
| 362 | + |
| 363 | + int getUpdateCount() { |
| 364 | + return this.updateCount; |
| 365 | + } |
| 366 | + |
| 367 | + } |
| 368 | + |
322 | 369 | } |
0 commit comments