CLOVER🍀

That was when it all began.

Spring BatchのトランザクションがRuntimeExceptionでもException(検査例外)でもロールバックすることを確認する

これは、なにをしたくて書いたもの?

Spring Frameworkでのトランザクション管理といえば、宣言的トランザクションを使うことが多いでしょう。

Data Access / Transaction Management / Declarative Transaction Management

一方で、宣言的トランザクションではデフォルトではRuntimeException(とError)がロールバック対象となり、Exception…いわゆる
検査例外はロールバックされないことも知られています。

In its default configuration, the Spring Framework’s transaction infrastructure code marks a transaction for rollback only in the case of runtime, unchecked exceptions. That is, when the thrown exception is an instance or subclass of RuntimeException. (Error instances also, by default, result in a rollback). Checked exceptions that are thrown from a transactional method do not result in rollback in the default configuration.

Data Access / Transaction Management / Rolling Back a Declarative Transaction

Spring Batchでは、トランザクション管理をSpring Batch側が行うということなのですが、スローされる例外がRuntimeExceptionなのか
Exceptionなのかで挙動が変わるのかどうかが気になったので、調べてみることにしました。

Spring Batchのトランザクション管理

まずは、Spring Batchのトランザクション管理に関するドキュメントを見てみましょう。

トランザクションに関する記述は、このあたりのドキュメントに書かれているようです。

Configuring a Step

Batch Processing and Transactions

主には、チャンク指向の処理で説明が書かれています。トランザクションの単位が、チャンクというデータのまとまりになるという
話ですね。

Chunk oriented processing refers to reading the data one at a time and creating 'chunks' that are written out within a transaction boundary. Once the number of items read equals the commit interval, the entire chunk is written out by the ItemWriter, and then the transaction is committed.

Configuring a Step / Chunk-oriented Processing

たとえば、チャンクのサイズが100なら、スキップ等を考えなければデータ100件ごとにひとつのトランザクションとして扱われることに
なります。

チャンクサイズは、コミット間隔としても説明されています。

As mentioned previously, a step reads in and writes out items, periodically committing using the supplied PlatformTransactionManager. With a commit-interval of 1, it commits after writing each individual item. This is less than ideal in many situations, since beginning and committing a transaction is expensive. Ideally, it is preferable to process as many items as possible in each transaction, which is completely dependent upon the type of data being processed and the resources with which the step is interacting.

Configuring a Step / Chunk-oriented Processing / The Commit Interval

ロールバック制御の説では、ItemWriterによって例外がスローされるとロールバックされることが書かれています。

By default, regardless of retry or skip, any exceptions thrown from the ItemWriter cause the transaction controlled by the Step to rollback.

Configuring a Step / Chunk-oriented Processing / Controlling Rollback

こう読むと、ItemReaderItemProcessorはどうなんでしょうね?

ItemReaderに関しては、JMSキューを扱う場合などのトランザクショナルなリソースを扱う際に、キューから取得したメッセージを
戻さないようにバッファリングしないよう設定する方法も書かれています。

Configuring a Step / Chunk-oriented Processing / Controlling Rollback / Transactional Readers

こう見ると、ItemReaderトランザクションの範囲に含まれていそうですね。そうであって欲しいですが。

ちなみに、Listenerとトランザクションの関係についてもドキュメントに書かれています。

Configuring a Step / Chunk-oriented Processing / Intercepting Step Execution

チャンクに対してTaskletの場合は、シンプルにトランザクションにラップされることが書かれています。

Each call to a Tasklet is wrapped in a transaction.

Configuring a Step / TaskletStep

一方で、こちらのドキュメント…付録にはバッチの構成のバリエーションとトランザクションの関係がまとめられていますが。

Batch Processing and Transactions

シンプルな処理パターンでは、ここまで見てきた情報(特にチャンク)が簡潔にまとめられているだけですね。

Appendix A: Batch Processing and Transactions / Simple Batching with No Retry

ドキュメントから得られる情報はこれくらいなので、あとは実際に動かして確認してみるとしましょう。

お題

今回のお題は、こちら。

  • チャンクとTasklet、それぞれでJobを構成する
  • Jobの内容は、Listに定義したデータ10件をJPAでデータベースに書き込む処理とする
  • チャンク
    • データを10件用意して、チャンクサイズは3件とする
    • 一定の件数を処理した後で、RuntimeExceptionおよびExceptionItemReaderItemProcessorItemWriterそれぞれから例外をスローして動作を確認する
  • Tasklet
    • データを10件用意して一定の件数を処理した後で、RuntimeExceptionおよびExceptionTaskletから例外をスローして動作を確認する

このバリエーションを確認してみます。

環境

今回の環境は、こちら。

$ java --version
openjdk 17.0.3 2022-04-19
OpenJDK Runtime Environment (build 17.0.3+7-Ubuntu-0ubuntu0.20.04.1)
OpenJDK 64-Bit Server VM (build 17.0.3+7-Ubuntu-0ubuntu0.20.04.1, mixed mode, sharing)


$ mvn --version
Apache Maven 3.8.5 (3599d3414f046de2324203b78ddcf9b5e4388aa0)
Maven home: $HOME/.sdkman/candidates/maven/current
Java version: 17.0.3, vendor: Private Build, runtime: /usr/lib/jvm/java-17-openjdk-amd64
Default locale: ja_JP, platform encoding: UTF-8
OS name: "linux", version: "5.4.0-110-generic", arch: "amd64", family: "unix"

データベースには、MySQLを使用します。MySQLは、172.17.0.2で動作しているものとします。

$ mysql --version
mysql  Ver 8.0.29 for Linux on x86_64 (MySQL Community Server - GPL)

プロジェクトを作成する

まずは、Spring Bootプロジェクトを作成します。依存関係には、batchdata-jpamysqlを加えました。

$ curl -s https://start.spring.io/starter.tgz \
  -d bootVersion=2.7.0 \
  -d javaVersion=17 \
  -d name=batch-transaction \
  -d groupId=org.littlewings \
  -d artifactId=batch-transaction \
  -d version=0.0.1-SNAPSHOT \
  -d packageName=org.littlewings.spring.batch \
  -d dependencies=batch,data-jpa,mysql \
  -d baseDir=batch-transaction | tar zxvf -

プロジェクト内に移動。

$ cd batch-transaction

生成されたソースコードは、削除しておきます。

$ rm src/main/java/org/littlewings/spring/batch/BatchTransactionApplication.java src/test/java/org/littlewings/spring/batch/BatchTransactionApplicationTests.java

Mavenの依存関係やプラグインの設定は、こちら。

 <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-batch</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.batch</groupId>
            <artifactId>spring-batch-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

今回のテーブル定義は、こちらにしました。書籍をお題にします。

src/main/resources/schema.sql

drop table if exists book;

create table book (
  isbn varchar(14),
  title varchar(100),
  price int,
  primary key(isbn)
);

対応するJPAのEntity。

src/main/java/org/littlewings/spring/batch/Book.java

package org.littlewings.spring.batch;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "book")
public class Book {
    @Id
    @Column(name = "isbn")
    String isbn;

    @Column(name = "title")
    String title;

    @Column(name = "price")
    Integer price;

    public static Book create(String isbn, String title, Integer price) {
        Book book = new Book();
        book.setIsbn(isbn);
        book.setTitle(title);
        book.setPrice(price);

        return book;
    }

    // getter/setterは省略
}

mainクラス。

src/main/java/org/littlewings/spring/batch/App.java

package org.littlewings.spring.batch;

import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableBatchProcessing
public class App {
    public static void main(String... args) {
        SpringApplication.run(App.class, args);
    }
}

設定。

src/main/resources/application.properties

spring.datasource.url=jdbc:mysql://172.17.0.2:3306/practice?characterEncoding=utf-8
spring.datasource.username=kazuhira
spring.datasource.password=password

spring.sql.init.mode=always
spring.batch.jdbc.initialize-schema=always

logging.level.org.springframework.batch.core.step=debug
logging.level.org.springframework.transaction=debug

アプリケーションが使用するテーブルや、Spring Batchが使用する使用するテーブルは起動時に作成するようにしました。

あとは、チャンクとTaskletJobを作成しつつ動作確認していきましょう。

チャンク

チャンクを構成する、ItemReaderItemProcessorItemWriterを以下のように作成。

ItemReader

src/main/java/org/littlewings/spring/batch/BookItemReader.java

package org.littlewings.spring.batch;

import java.util.Iterator;
import java.util.List;

import org.springframework.batch.item.NonTransientResourceException;
import org.springframework.batch.item.ParseException;
import org.springframework.batch.item.UnexpectedInputException;
import org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader;
import org.springframework.batch.item.support.AbstractItemStreamItemReader;

public class BookItemReader extends AbstractItemStreamItemReader<Book> {
    Iterator<Book> bookIterator;
    int currentCount = 0;

    public BookItemReader() {
        List<Book> books = List.of(
                Book.create("978-4798142470", "Spring徹底入門 Spring FrameworkによるJavaアプリケーション開発", 4400),
                Book.create("978-4774182179", "[改訂新版]Spring入門 ――Javaフレームワーク・より良い設計とアーキテクチャ", 4180),
                Book.create("978-1492076988", "Spring Boot: Up and Running: Building Cloud Native Java and Kotlin Applications", 6265),
                Book.create("978-1484237236", "The Definitive Guide to Spring Batch: Modern Finite Batch Processing in the Cloud", 7361),
                Book.create("978-4798161488", "MySQL徹底入門 第4版 MySQL 8.0対応", 4180),
                Book.create("978-4797393118", "基礎からのMySQL 第3版 (基礎からシリーズ)", 6038),
                Book.create("978-4873116389", "実践ハイパフォーマンスMySQL 第3版", 5280),
                Book.create("978-4295000198", "やさしく学べるMySQL運用・管理入門【5.7対応】", 2860),
                Book.create("978-4798147406", "詳解MySQL 5.7 止まらぬ進化に乗り遅れないためのテクニカルガイド (NEXT ONE)", 3960),
                Book.create("978-4774170206", "MariaDB&MySQL全機能バイブル", 3860)
        );

        bookIterator = books.iterator();
    }

    @Override
    public Book read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException {
        currentCount++;

        if (currentCount > 7) {
            // throw new RuntimeException("Oops!!");
            // throw new Exception("Oops!!");
        }

        if (bookIterator.hasNext()) {
            return bookIterator.next();
        } else {
            return null;
        }
    }
}

10件の書籍データを扱うことにします。

ItemProcessor

src/main/java/org/littlewings/spring/batch/BookItemProcessor.java

package org.littlewings.spring.batch;

import org.springframework.batch.item.ItemProcessor;

public class BookItemProcessor implements ItemProcessor<Book, Book> {
    int currentCount = 0;

    @Override
    public Book process(Book item) throws Exception {
        currentCount++;

        if (currentCount > 7) {
            // throw new RuntimeException("Oops!!");
            // throw new Exception("Oops!!");
        }

        return item;
    }
}

ItemWriter

src/main/java/org/littlewings/spring/batch/BookItemWriter.java

package org.littlewings.spring.batch;

import java.util.List;

import javax.persistence.EntityManager;

import org.springframework.batch.item.support.AbstractItemStreamItemWriter;
import org.springframework.beans.factory.annotation.Autowired;

public class BookItemWriter extends AbstractItemStreamItemWriter<Book> {
    @Autowired
    EntityManager entityManager;

    int currentCount = 0;

    @Override
    public void write(List<? extends Book> items) throws Exception {
        for (Book book : items) {
            currentCount++;

            if (currentCount > 7) {
                // throw new RuntimeException("Oops!!");
                // throw new Exception("Oops!!");
            }

            entityManager.persist(book);
            entityManager.flush();
        }
    }
}

ItemWriterは、データ1件ずつフラッシュするようにしています。SQLをすぐに実行したいからですね。

いずれにも共通のパターンがありますが、7件目のデータを扱うところでコメントアウトを解除すると例外をスローするようになっています。
RuntimeExceptionおよびExceptionです。

        if (currentCount > 7) {
            // throw new RuntimeException("Oops!!");
            // throw new Exception("Oops!!");
        }

ItemReaderItemProcessorItemWriterそれぞれでこのコメントアウトの解除を変化させつつ、どのような動きをするかを見ていきます。

Jobの定義はこちら。

src/main/java/org/littlewings/spring/batch/ChunkJobConfig.java

package org.littlewings.spring.batch;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepScope;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ChunkJobConfig {
    @Bean
    public Job chunkJob(JobBuilderFactory jobBuilderFactory) {
        return jobBuilderFactory
                .get("chunkJob")
                .incrementer(new RunIdIncrementer())
                .start(chunkStep(null))
                .build();
    }

    @Bean
    public Step chunkStep(StepBuilderFactory stepBuilderFactory) {
        return stepBuilderFactory
                .get("chunkStep")
                .<Book, Book>chunk(3)
                .reader(bookItemReader())
                .processor(bookItemProcessor())
                .writer(bookItemWriter())
                .build();
    }

    @Bean
    @StepScope
    public BookItemReader bookItemReader() {
        return new BookItemReader();
    }

    @Bean
    @StepScope
    public BookItemProcessor bookItemProcessor() {
        return new BookItemProcessor();
    }

    @Bean
    @StepScope
    public BookItemWriter bookItemWriter() {
        return new BookItemWriter();
    }
}

チャンクサイズは3としています。つまり、コメントアウトを解除すると、3つ目のチャンクの処理中に例外がスローされることに
なりますね。

では、確認していきましょう。

確認は、ソースコードを変更しつつパッケージングして

$ mvn package

Jobを指定して実行することにします。

$ java -Dspring.batch.job.names=chunkJob -jar target/batch-transaction-0.0.1-SNAPSHOT.jar
正常に終了する場合

まずは、正常に終了する場合から。

実行時のログはこちら。

2022-05-27 01:22:22.882  INFO 20890 --- [           main] o.s.b.a.b.JobLauncherApplicationRunner   : Running default command line with: []
2022-05-27 01:22:23.417  INFO 20890 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=chunkJob]] launched with the following parameters: [{run.id=1}]
2022-05-27 01:22:23.628  INFO 20890 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Executing step: [chunkStep]
2022-05-27 01:22:23.629 DEBUG 20890 --- [           main] o.s.batch.core.step.AbstractStep         : Executing: id=1
2022-05-27 01:22:23.944 DEBUG 20890 --- [           main] o.s.b.c.step.item.ChunkOrientedTasklet   : Inputs not busy, ended: false
2022-05-27 01:22:23.945 DEBUG 20890 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Applying contribution: [StepContribution: read=3, written=3, filtered=0, readSkips=0, writeSkips=0, processSkips=0, exitStatus=EXECUTING]
2022-05-27 01:22:23.953 DEBUG 20890 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Saving step execution before commit: StepExecution: id=1, version=1, name=chunkStep, status=STARTED, exitStatus=EXECUTING, readCount=3, filterCount=0, writeCount=3 readSkipCount=0, writeSkipCount=0, processSkipCount=0, commitCount=1, rollbackCount=0, exitDescription=
2022-05-27 01:22:24.072 DEBUG 20890 --- [           main] o.s.b.c.step.item.ChunkOrientedTasklet   : Inputs not busy, ended: false
2022-05-27 01:22:24.073 DEBUG 20890 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Applying contribution: [StepContribution: read=3, written=3, filtered=0, readSkips=0, writeSkips=0, processSkips=0, exitStatus=EXECUTING]
2022-05-27 01:22:24.076 DEBUG 20890 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Saving step execution before commit: StepExecution: id=1, version=2, name=chunkStep, status=STARTED, exitStatus=EXECUTING, readCount=6, filterCount=0, writeCount=6 readSkipCount=0, writeSkipCount=0, processSkipCount=0, commitCount=2, rollbackCount=0, exitDescription=
2022-05-27 01:22:24.343 DEBUG 20890 --- [           main] o.s.b.c.step.item.ChunkOrientedTasklet   : Inputs not busy, ended: false
2022-05-27 01:22:24.344 DEBUG 20890 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Applying contribution: [StepContribution: read=3, written=3, filtered=0, readSkips=0, writeSkips=0, processSkips=0, exitStatus=EXECUTING]
2022-05-27 01:22:24.345 DEBUG 20890 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Saving step execution before commit: StepExecution: id=1, version=3, name=chunkStep, status=STARTED, exitStatus=EXECUTING, readCount=9, filterCount=0, writeCount=9 readSkipCount=0, writeSkipCount=0, processSkipCount=0, commitCount=3, rollbackCount=0, exitDescription=
2022-05-27 01:22:24.510 DEBUG 20890 --- [           main] o.s.b.c.step.item.ChunkOrientedTasklet   : Inputs not busy, ended: true
2022-05-27 01:22:24.510 DEBUG 20890 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Applying contribution: [StepContribution: read=1, written=1, filtered=0, readSkips=0, writeSkips=0, processSkips=0, exitStatus=EXECUTING]
2022-05-27 01:22:24.512 DEBUG 20890 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Saving step execution before commit: StepExecution: id=1, version=4, name=chunkStep, status=STARTED, exitStatus=EXECUTING, readCount=10, filterCount=0, writeCount=10 readSkipCount=0, writeSkipCount=0, processSkipCount=0, commitCount=4, rollbackCount=0, exitDescription=
2022-05-27 01:22:24.576 DEBUG 20890 --- [           main] o.s.batch.core.step.AbstractStep         : Step execution success: id=1
2022-05-27 01:22:24.580  INFO 20890 --- [           main] o.s.batch.core.step.AbstractStep         : Step: [chunkStep] executed in 950ms
2022-05-27 01:22:24.698 DEBUG 20890 --- [           main] o.s.batch.core.step.AbstractStep         : Step execution complete: StepExecution: id=1, version=6, name=chunkStep, status=COMPLETED, exitStatus=COMPLETED, readCount=10, filterCount=0, writeCount=10 readSkipCount=0, writeSkipCount=0, processSkipCount=0, commitCount=4, rollbackCount=0
2022-05-27 01:22:24.757  INFO 20890 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=chunkJob]] completed with the following parameters: [{run.id=1}] and the following status: [COMPLETED] in 1s268ms

データはこうなりました。

mysql> select * from book;
+----------------+---------------------------------------------------------------------------------------------------------+-------+
| isbn           | title                                                                                                   | price |
+----------------+---------------------------------------------------------------------------------------------------------+-------+
| 978-1484237236 | The Definitive Guide to Spring Batch: Modern Finite Batch Processing in the Cloud                       |  7361 |
| 978-1492076988 | Spring Boot: Up and Running: Building Cloud Native Java and Kotlin Applications                         |  6265 |
| 978-4295000198 | やさしく学べるMySQL運用・管理入門【5.7対応】                                                            |  2860 |
| 978-4774170206 | MariaDB&MySQL全機能バイブル                                                                             |  3860 |
| 978-4774182179 | [改訂新版]Spring入門 ――Javaフレームワーク・より良い設計とアーキテクチャ                                 |  4180 |
| 978-4797393118 | 基礎からのMySQL 第3版 (基礎からシリーズ)                                                                |  6038 |
| 978-4798142470 | Spring徹底入門 Spring FrameworkによるJavaアプリケーション開発                                           |  4400 |
| 978-4798147406 | 詳解MySQL 5.7 止まらぬ進化に乗り遅れないためのテクニカルガイド (NEXT ONE)                               |  3960 |
| 978-4798161488 | MySQL徹底入門 第4版 MySQL 8.0対応                                                                       |  4180 |
| 978-4873116389 | 実践ハイパフォーマンスMySQL 第3版                                                                       |  5280 |
+----------------+---------------------------------------------------------------------------------------------------------+-------+
10 rows in set (0.00 sec)
ItemReaderが例外をスローする場合

次は、ItemReaderが例外をスローする場合です。

まずは、RuntimeExceptionをスローしてみます。

    @Override
    public Book read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException {
        currentCount++;

        if (currentCount > 7) {
            throw new RuntimeException("Oops!!");
            // throw new Exception("Oops!!");
        }

        if (bookIterator.hasNext()) {
            return bookIterator.next();
        } else {
            return null;
        }
    }

ログ。

2022-05-27 01:23:27.851  INFO 21357 --- [           main] o.s.b.a.b.JobLauncherApplicationRunner   : Running default command line with: []
2022-05-27 01:23:28.190  INFO 21357 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=chunkJob]] launched with the following parameters: [{run.id=2}]
2022-05-27 01:23:28.312  INFO 21357 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Executing step: [chunkStep]
2022-05-27 01:23:28.312 DEBUG 21357 --- [           main] o.s.batch.core.step.AbstractStep         : Executing: id=2
2022-05-27 01:23:28.531 DEBUG 21357 --- [           main] o.s.b.c.step.item.ChunkOrientedTasklet   : Inputs not busy, ended: false
2022-05-27 01:23:28.532 DEBUG 21357 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Applying contribution: [StepContribution: read=3, written=3, filtered=0, readSkips=0, writeSkips=0, processSkips=0, exitStatus=EXECUTING]
2022-05-27 01:23:28.536 DEBUG 21357 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Saving step execution before commit: StepExecution: id=2, version=1, name=chunkStep, status=STARTED, exitStatus=EXECUTING, readCount=3, filterCount=0, writeCount=3 readSkipCount=0, writeSkipCount=0, processSkipCount=0, commitCount=1, rollbackCount=0, exitDescription=
2022-05-27 01:23:28.575 DEBUG 21357 --- [           main] o.s.b.c.step.item.ChunkOrientedTasklet   : Inputs not busy, ended: false
2022-05-27 01:23:28.575 DEBUG 21357 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Applying contribution: [StepContribution: read=3, written=3, filtered=0, readSkips=0, writeSkips=0, processSkips=0, exitStatus=EXECUTING]
2022-05-27 01:23:28.577 DEBUG 21357 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Saving step execution before commit: StepExecution: id=2, version=2, name=chunkStep, status=STARTED, exitStatus=EXECUTING, readCount=6, filterCount=0, writeCount=6 readSkipCount=0, writeSkipCount=0, processSkipCount=0, commitCount=2, rollbackCount=0, exitDescription=
2022-05-27 01:23:28.607 DEBUG 21357 --- [           main] o.s.b.c.step.item.SimpleChunkProvider    : Oops!! : java.lang.RuntimeException
2022-05-27 01:23:28.607 DEBUG 21357 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Applying contribution: [StepContribution: read=1, written=0, filtered=0, readSkips=0, writeSkips=0, processSkips=0, exitStatus=EXECUTING]
2022-05-27 01:23:28.607 DEBUG 21357 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Rollback for RuntimeException: java.lang.RuntimeException: Oops!!
2022-05-27 01:23:28.612 DEBUG 21357 --- [           main] o.s.t.support.TransactionTemplate        : Initiating transaction rollback on application exception

java.lang.RuntimeException: Oops!!
        at org.littlewings.spring.batch.BookItemReader.read(BookItemReader.java:38) ~[classes!/:0.0.1-SNAPSHOT]
        at org.littlewings.spring.batch.BookItemReader.read(BookItemReader.java:12) ~[classes!/:0.0.1-SNAPSHOT]
        at org.littlewings.spring.batch.BookItemReader$$FastClassBySpringCGLIB$$29093ae.invoke(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:137) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:124) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.littlewings.spring.batch.BookItemReader$$EnhancerBySpringCGLIB$$9a856c3.read(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
        at org.springframework.batch.core.step.item.SimpleChunkProvider.doRead(SimpleChunkProvider.java:99) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.SimpleChunkProvider.read(SimpleChunkProvider.java:180) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.SimpleChunkProvider$1.doInIteration(SimpleChunkProvider.java:126) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:375) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:145) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.SimpleChunkProvider.provide(SimpleChunkProvider.java:118) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.ChunkOrientedTasklet.execute(ChunkOrientedTasklet.java:71) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:407) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:331) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140) ~[spring-tx-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.step.tasklet.TaskletStep$2.doInChunkContext(TaskletStep.java:273) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:82) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:375) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:145) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:258) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:208) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:152) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.AbstractJob.handleStep(AbstractJob.java:413) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.SimpleJob.doExecute(SimpleJob.java:136) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:320) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:149) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50) ~[spring-core-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:140) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
        at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:128) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at jdk.proxy2/jdk.proxy2.$Proxy83.run(Unknown Source) ~[na:na]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.execute(JobLauncherApplicationRunner.java:199) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.executeLocalJobs(JobLauncherApplicationRunner.java:173) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.launchJobFromProperties(JobLauncherApplicationRunner.java:160) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:155) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:150) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:762) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:752) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.littlewings.spring.batch.App.main(App.java:11) ~[classes!/:0.0.1-SNAPSHOT]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
        at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:108) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:58) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:65) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]

2022-05-27 01:23:28.619 ERROR 21357 --- [           main] o.s.batch.core.step.AbstractStep         : Encountered an error executing step chunkStep in job chunkJob

java.lang.RuntimeException: Oops!!
        at org.littlewings.spring.batch.BookItemReader.read(BookItemReader.java:38) ~[classes!/:0.0.1-SNAPSHOT]
        at org.littlewings.spring.batch.BookItemReader.read(BookItemReader.java:12) ~[classes!/:0.0.1-SNAPSHOT]
        at org.littlewings.spring.batch.BookItemReader$$FastClassBySpringCGLIB$$29093ae.invoke(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:137) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:124) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.littlewings.spring.batch.BookItemReader$$EnhancerBySpringCGLIB$$9a856c3.read(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
        at org.springframework.batch.core.step.item.SimpleChunkProvider.doRead(SimpleChunkProvider.java:99) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.SimpleChunkProvider.read(SimpleChunkProvider.java:180) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.SimpleChunkProvider$1.doInIteration(SimpleChunkProvider.java:126) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:375) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:145) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.SimpleChunkProvider.provide(SimpleChunkProvider.java:118) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.ChunkOrientedTasklet.execute(ChunkOrientedTasklet.java:71) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:407) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:331) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140) ~[spring-tx-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.step.tasklet.TaskletStep$2.doInChunkContext(TaskletStep.java:273) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:82) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:375) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:145) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:258) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:208) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:152) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.AbstractJob.handleStep(AbstractJob.java:413) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.SimpleJob.doExecute(SimpleJob.java:136) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:320) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:149) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50) ~[spring-core-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:140) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
        at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:128) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at jdk.proxy2/jdk.proxy2.$Proxy83.run(Unknown Source) ~[na:na]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.execute(JobLauncherApplicationRunner.java:199) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.executeLocalJobs(JobLauncherApplicationRunner.java:173) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.launchJobFromProperties(JobLauncherApplicationRunner.java:160) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:155) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:150) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:762) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:752) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.littlewings.spring.batch.App.main(App.java:11) ~[classes!/:0.0.1-SNAPSHOT]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
        at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:108) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:58) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:65) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]

2022-05-27 01:23:28.625  INFO 21357 --- [           main] o.s.batch.core.step.AbstractStep         : Step: [chunkStep] executed in 313ms
2022-05-27 01:23:28.658 DEBUG 21357 --- [           main] o.s.batch.core.step.AbstractStep         : Step execution complete: StepExecution: id=2, version=4, name=chunkStep, status=FAILED, exitStatus=FAILED, readCount=7, filterCount=0, writeCount=6 readSkipCount=0, writeSkipCount=0, processSkipCount=0, commitCount=2, rollbackCount=1
2022-05-27 01:23:28.693  INFO 21357 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=chunkJob]] completed with the following parameters: [{run.id=2}] and the following status: [FAILED] in 448ms

ロールバックを表すログが出力されています。ちなみに、DEBUGレベルですね。

2022-05-27 01:23:28.607 DEBUG 21357 --- [           main] o.s.b.c.step.item.SimpleChunkProvider    : Oops!! : java.lang.RuntimeException
2022-05-27 01:23:28.607 DEBUG 21357 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Applying contribution: [StepContribution: read=1, written=0, filtered=0, readSkips=0, writeSkips=0, processSkips=0, exitStatus=EXECUTING]
2022-05-27 01:23:28.607 DEBUG 21357 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Rollback for RuntimeException: java.lang.RuntimeException: Oops!!
2022-05-27 01:23:28.612 DEBUG 21357 --- [           main] o.s.t.support.TransactionTemplate        : Initiating transaction rollback on application exception

java.lang.RuntimeException: Oops!!

データは、チャンク2つ分だけが入っていますね。3つ目のチャンクは入っていません。

mysql> select * from book;
+----------------+---------------------------------------------------------------------------------------------------------+-------+
| isbn           | title                                                                                                   | price |
+----------------+---------------------------------------------------------------------------------------------------------+-------+
| 978-1484237236 | The Definitive Guide to Spring Batch: Modern Finite Batch Processing in the Cloud                       |  7361 |
| 978-1492076988 | Spring Boot: Up and Running: Building Cloud Native Java and Kotlin Applications                         |  6265 |
| 978-4774182179 | [改訂新版]Spring入門 ――Javaフレームワーク・より良い設計とアーキテクチャ                                 |  4180 |
| 978-4797393118 | 基礎からのMySQL 第3版 (基礎からシリーズ)                                                                |  6038 |
| 978-4798142470 | Spring徹底入門 Spring FrameworkによるJavaアプリケーション開発                                           |  4400 |
| 978-4798161488 | MySQL徹底入門 第4版 MySQL 8.0対応                                                                       |  4180 |
+----------------+---------------------------------------------------------------------------------------------------------+-------+
6 rows in set (0.01 sec)

今度は、Exceptionをスローしてみます。

    @Override
    public Book read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException {
        currentCount++;

        if (currentCount > 7) {
            // throw new RuntimeException("Oops!!");
            throw new Exception("Oops!!");
        }

        if (bookIterator.hasNext()) {
            return bookIterator.next();
        } else {
            return null;
        }
    }

ログ。

2022-05-27 01:25:29.476  INFO 21604 --- [           main] o.s.b.a.b.JobLauncherApplicationRunner   : Running default command line with: []
2022-05-27 01:25:29.829  INFO 21604 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=chunkJob]] launched with the following parameters: [{run.id=3}]
2022-05-27 01:25:29.931  INFO 21604 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Executing step: [chunkStep]
2022-05-27 01:25:29.932 DEBUG 21604 --- [           main] o.s.batch.core.step.AbstractStep         : Executing: id=3
2022-05-27 01:25:30.098 DEBUG 21604 --- [           main] o.s.b.c.step.item.ChunkOrientedTasklet   : Inputs not busy, ended: false
2022-05-27 01:25:30.098 DEBUG 21604 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Applying contribution: [StepContribution: read=3, written=3, filtered=0, readSkips=0, writeSkips=0, processSkips=0, exitStatus=EXECUTING]
2022-05-27 01:25:30.101 DEBUG 21604 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Saving step execution before commit: StepExecution: id=3, version=1, name=chunkStep, status=STARTED, exitStatus=EXECUTING, readCount=3, filterCount=0, writeCount=3 readSkipCount=0, writeSkipCount=0, processSkipCount=0, commitCount=1, rollbackCount=0, exitDescription=
2022-05-27 01:25:30.145 DEBUG 21604 --- [           main] o.s.b.c.step.item.ChunkOrientedTasklet   : Inputs not busy, ended: false
2022-05-27 01:25:30.145 DEBUG 21604 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Applying contribution: [StepContribution: read=3, written=3, filtered=0, readSkips=0, writeSkips=0, processSkips=0, exitStatus=EXECUTING]
2022-05-27 01:25:30.147 DEBUG 21604 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Saving step execution before commit: StepExecution: id=3, version=2, name=chunkStep, status=STARTED, exitStatus=EXECUTING, readCount=6, filterCount=0, writeCount=6 readSkipCount=0, writeSkipCount=0, processSkipCount=0, commitCount=2, rollbackCount=0, exitDescription=
2022-05-27 01:25:30.194 DEBUG 21604 --- [           main] o.s.b.c.step.item.SimpleChunkProvider    : Oops!! : java.lang.Exception
2022-05-27 01:25:30.195 DEBUG 21604 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Applying contribution: [StepContribution: read=1, written=0, filtered=0, readSkips=0, writeSkips=0, processSkips=0, exitStatus=EXECUTING]
2022-05-27 01:25:30.195 DEBUG 21604 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Rollback for RuntimeException: org.springframework.batch.repeat.RepeatException: Exception in batch process; nested exception is java.lang.Exception: Oops!!
2022-05-27 01:25:30.201 DEBUG 21604 --- [           main] o.s.t.support.TransactionTemplate        : Initiating transaction rollback on application exception

org.springframework.batch.repeat.RepeatException: Exception in batch process; nested exception is java.lang.Exception: Oops!!
        at org.springframework.batch.repeat.support.RepeatTemplate.rethrow(RepeatTemplate.java:320) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:256) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:145) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.SimpleChunkProvider.provide(SimpleChunkProvider.java:118) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.ChunkOrientedTasklet.execute(ChunkOrientedTasklet.java:71) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:407) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:331) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140) ~[spring-tx-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.step.tasklet.TaskletStep$2.doInChunkContext(TaskletStep.java:273) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:82) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:375) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:145) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:258) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:208) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:152) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.AbstractJob.handleStep(AbstractJob.java:413) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.SimpleJob.doExecute(SimpleJob.java:136) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:320) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:149) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50) ~[spring-core-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:140) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
        at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:128) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at jdk.proxy2/jdk.proxy2.$Proxy83.run(Unknown Source) ~[na:na]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.execute(JobLauncherApplicationRunner.java:199) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.executeLocalJobs(JobLauncherApplicationRunner.java:173) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.launchJobFromProperties(JobLauncherApplicationRunner.java:160) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:155) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:150) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:762) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:752) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.littlewings.spring.batch.App.main(App.java:11) ~[classes!/:0.0.1-SNAPSHOT]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
        at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:108) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:58) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:65) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
Caused by: java.lang.Exception: Oops!!
        at org.littlewings.spring.batch.BookItemReader.read(BookItemReader.java:39) ~[classes!/:0.0.1-SNAPSHOT]
        at org.littlewings.spring.batch.BookItemReader.read(BookItemReader.java:12) ~[classes!/:0.0.1-SNAPSHOT]
        at org.littlewings.spring.batch.BookItemReader$$FastClassBySpringCGLIB$$29093ae.invoke(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:137) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:124) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.littlewings.spring.batch.BookItemReader$$EnhancerBySpringCGLIB$$b4130ec4.read(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
        at org.springframework.batch.core.step.item.SimpleChunkProvider.doRead(SimpleChunkProvider.java:99) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.SimpleChunkProvider.read(SimpleChunkProvider.java:180) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.SimpleChunkProvider$1.doInIteration(SimpleChunkProvider.java:126) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:375) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        ... 50 common frames omitted

2022-05-27 01:25:30.208 ERROR 21604 --- [           main] o.s.batch.core.step.AbstractStep         : Encountered an error executing step chunkStep in job chunkJob

java.lang.Exception: Oops!!
        at org.littlewings.spring.batch.BookItemReader.read(BookItemReader.java:39) ~[classes!/:0.0.1-SNAPSHOT]
        at org.littlewings.spring.batch.BookItemReader.read(BookItemReader.java:12) ~[classes!/:0.0.1-SNAPSHOT]
        at org.littlewings.spring.batch.BookItemReader$$FastClassBySpringCGLIB$$29093ae.invoke(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:137) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:124) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.littlewings.spring.batch.BookItemReader$$EnhancerBySpringCGLIB$$b4130ec4.read(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
        at org.springframework.batch.core.step.item.SimpleChunkProvider.doRead(SimpleChunkProvider.java:99) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.SimpleChunkProvider.read(SimpleChunkProvider.java:180) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.SimpleChunkProvider$1.doInIteration(SimpleChunkProvider.java:126) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:375) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:145) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.SimpleChunkProvider.provide(SimpleChunkProvider.java:118) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.ChunkOrientedTasklet.execute(ChunkOrientedTasklet.java:71) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:407) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:331) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140) ~[spring-tx-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.step.tasklet.TaskletStep$2.doInChunkContext(TaskletStep.java:273) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:82) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:375) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:145) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:258) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:208) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:152) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.AbstractJob.handleStep(AbstractJob.java:413) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.SimpleJob.doExecute(SimpleJob.java:136) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:320) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:149) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50) ~[spring-core-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:140) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
        at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:128) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at jdk.proxy2/jdk.proxy2.$Proxy83.run(Unknown Source) ~[na:na]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.execute(JobLauncherApplicationRunner.java:199) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.executeLocalJobs(JobLauncherApplicationRunner.java:173) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.launchJobFromProperties(JobLauncherApplicationRunner.java:160) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:155) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:150) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:762) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:752) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.littlewings.spring.batch.App.main(App.java:11) ~[classes!/:0.0.1-SNAPSHOT]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
        at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:108) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:58) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:65) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]

2022-05-27 01:25:30.215  INFO 21604 --- [           main] o.s.batch.core.step.AbstractStep         : Step: [chunkStep] executed in 283ms
2022-05-27 01:25:30.258 DEBUG 21604 --- [           main] o.s.batch.core.step.AbstractStep         : Step execution complete: StepExecution: id=3, version=4, name=chunkStep, status=FAILED, exitStatus=FAILED, readCount=7, filterCount=0, writeCount=6 readSkipCount=0, writeSkipCount=0, processSkipCount=0, commitCount=2, rollbackCount=1
2022-05-27 01:25:30.315  INFO 21604 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=chunkJob]] completed with the following parameters: [{run.id=3}] and the following status: [FAILED] in 412ms

ロールバックされたようです。

登録されたデータ数には、変化がありません。

mysql> select * from book;
+----------------+---------------------------------------------------------------------------------------------------------+-------+
| isbn           | title                                                                                                   | price |
+----------------+---------------------------------------------------------------------------------------------------------+-------+
| 978-1484237236 | The Definitive Guide to Spring Batch: Modern Finite Batch Processing in the Cloud                       |  7361 |
| 978-1492076988 | Spring Boot: Up and Running: Building Cloud Native Java and Kotlin Applications                         |  6265 |
| 978-4774182179 | [改訂新版]Spring入門 ――Javaフレームワーク・より良い設計とアーキテクチャ                                 |  4180 |
| 978-4797393118 | 基礎からのMySQL 第3版 (基礎からシリーズ)                                                                |  6038 |
| 978-4798142470 | Spring徹底入門 Spring FrameworkによるJavaアプリケーション開発                                           |  4400 |
| 978-4798161488 | MySQL徹底入門 第4版 MySQL 8.0対応                                                                       |  4180 |
+----------------+---------------------------------------------------------------------------------------------------------+-------+
6 rows in set (0.00 sec)

ログに関しては、よくよく見るとスタックトレースが変わっています。

RuntimeExceptionの場合。

2022-05-27 01:23:28.607 DEBUG 21357 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Rollback for RuntimeException: java.lang.RuntimeException: Oops!!
2022-05-27 01:23:28.612 DEBUG 21357 --- [           main] o.s.t.support.TransactionTemplate        : Initiating transaction rollback on application exception

java.lang.RuntimeException: Oops!!
        at org.littlewings.spring.batch.BookItemReader.read(BookItemReader.java:38) ~[classes!/:0.0.1-SNAPSHOT]
        at org.littlewings.spring.batch.BookItemReader.read(BookItemReader.java:12) ~[classes!/:0.0.1-SNAPSHOT]
        at org.littlewings.spring.batch.BookItemReader$$FastClassBySpringCGLIB$$29093ae.invoke(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.20.jar!/:5.3.20]

〜省略〜

Exceptionの場合。

2022-05-27 01:25:30.195 DEBUG 21604 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Rollback for RuntimeException: org.springframework.batch.repeat.RepeatException: Exception in batch process; nested exception is java.lang.Exception: Oops!!
2022-05-27 01:25:30.201 DEBUG 21604 --- [           main] o.s.t.support.TransactionTemplate        : Initiating transaction rollback on application exception

org.springframework.batch.repeat.RepeatException: Exception in batch process; nested exception is java.lang.Exception: Oops!!
        at org.springframework.batch.repeat.support.RepeatTemplate.rethrow(RepeatTemplate.java:320) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:256) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:145) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.SimpleChunkProvider.provide(SimpleChunkProvider.java:118) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.ChunkOrientedTasklet.execute(ChunkOrientedTasklet.java:71) ~[spring-batch-core-4.3.6.jar!/:4.3.6]

〜省略〜

Caused by: java.lang.Exception: Oops!!
        at org.littlewings.spring.batch.BookItemReader.read(BookItemReader.java:39) ~[classes!/:0.0.1-SNAPSHOT]
        at org.littlewings.spring.batch.BookItemReader.read(BookItemReader.java:12) ~[classes!/:0.0.1-SNAPSHOT]
        at org.littlewings.spring.batch.BookItemReader$$FastClassBySpringCGLIB$$29093ae.invoke(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.20.jar!/:5.3.20]

〜省略〜

よく見ると、Exceptionの方はネストした例外になっています。そしてRuntimeExceptionロールバックしたことになっています。

2022-05-27 01:25:30.195 DEBUG 21604 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Rollback for RuntimeException: org.springframework.batch.repeat.RepeatException: Exception in batch process; nested exception is java.lang.Exception: Oops!!

どうやら、ラップして再スローしているようですね。RepeatExceptionをスローしたことになっています。

2022-05-27 01:25:30.201 DEBUG 21604 --- [           main] o.s.t.support.TransactionTemplate        : Initiating transaction rollback on application exception

org.springframework.batch.repeat.RepeatException: Exception in batch process; nested exception is java.lang.Exception: Oops!!
        at org.springframework.batch.repeat.support.RepeatTemplate.rethrow(RepeatTemplate.java:320) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]

RuntimeExceptionをスローした時の方が、素直な動きですね。

結論としては、RuntimeExceptionでもExceptionでもロールバックすることがわかりました。

ItemReaderは、いったん元に戻します。

    @Override
    public Book read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException {
        currentCount++;

        if (currentCount > 7) {
            // throw new RuntimeException("Oops!!");
            // throw new Exception("Oops!!");
        }

        if (bookIterator.hasNext()) {
            return bookIterator.next();
        } else {
            return null;
        }
    }
ItemProcessorが例外をスローする場合

次は、ItemProcessorに移りましょう。

RuntimeExceptionをスローしてみます。

    @Override
    public Book process(Book item) throws Exception {
        currentCount++;

        if (currentCount > 7) {
            throw new RuntimeException("Oops!!");
            // throw new Exception("Oops!!");
        }

        return item;
    }

実行ログ。

2022-05-27 01:29:23.714  INFO 21982 --- [           main] o.s.b.a.b.JobLauncherApplicationRunner   : Running default command line with: []
2022-05-27 01:29:24.011  INFO 21982 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=chunkJob]] launched with the following parameters: [{run.id=4}]
2022-05-27 01:29:24.163  INFO 21982 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Executing step: [chunkStep]
2022-05-27 01:29:24.163 DEBUG 21982 --- [           main] o.s.batch.core.step.AbstractStep         : Executing: id=4
2022-05-27 01:29:24.390 DEBUG 21982 --- [           main] o.s.b.c.step.item.ChunkOrientedTasklet   : Inputs not busy, ended: false
2022-05-27 01:29:24.390 DEBUG 21982 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Applying contribution: [StepContribution: read=3, written=3, filtered=0, readSkips=0, writeSkips=0, processSkips=0, exitStatus=EXECUTING]
2022-05-27 01:29:24.393 DEBUG 21982 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Saving step execution before commit: StepExecution: id=4, version=1, name=chunkStep, status=STARTED, exitStatus=EXECUTING, readCount=3, filterCount=0, writeCount=3 readSkipCount=0, writeSkipCount=0, processSkipCount=0, commitCount=1, rollbackCount=0, exitDescription=
2022-05-27 01:29:24.429 DEBUG 21982 --- [           main] o.s.b.c.step.item.ChunkOrientedTasklet   : Inputs not busy, ended: false
2022-05-27 01:29:24.429 DEBUG 21982 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Applying contribution: [StepContribution: read=3, written=3, filtered=0, readSkips=0, writeSkips=0, processSkips=0, exitStatus=EXECUTING]
2022-05-27 01:29:24.431 DEBUG 21982 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Saving step execution before commit: StepExecution: id=4, version=2, name=chunkStep, status=STARTED, exitStatus=EXECUTING, readCount=6, filterCount=0, writeCount=6 readSkipCount=0, writeSkipCount=0, processSkipCount=0, commitCount=2, rollbackCount=0, exitDescription=
2022-05-27 01:29:24.461 DEBUG 21982 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Applying contribution: [StepContribution: read=3, written=0, filtered=0, readSkips=0, writeSkips=0, processSkips=0, exitStatus=EXECUTING]
2022-05-27 01:29:24.461 DEBUG 21982 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Rollback for RuntimeException: java.lang.RuntimeException: Oops!!
2022-05-27 01:29:24.465 DEBUG 21982 --- [           main] o.s.t.support.TransactionTemplate        : Initiating transaction rollback on application exception

java.lang.RuntimeException: Oops!!
        at org.littlewings.spring.batch.BookItemProcessor.process(BookItemProcessor.java:13) ~[classes!/:0.0.1-SNAPSHOT]
        at org.littlewings.spring.batch.BookItemProcessor.process(BookItemProcessor.java:5) ~[classes!/:0.0.1-SNAPSHOT]
        at org.littlewings.spring.batch.BookItemProcessor$$FastClassBySpringCGLIB$$775ca5c7.invoke(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:137) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:124) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.littlewings.spring.batch.BookItemProcessor$$EnhancerBySpringCGLIB$$acf7a11d.process(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
        at org.springframework.batch.core.step.item.SimpleChunkProcessor.doProcess(SimpleChunkProcessor.java:134) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.SimpleChunkProcessor.transform(SimpleChunkProcessor.java:319) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.SimpleChunkProcessor.process(SimpleChunkProcessor.java:210) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.ChunkOrientedTasklet.execute(ChunkOrientedTasklet.java:77) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:407) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:331) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140) ~[spring-tx-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.step.tasklet.TaskletStep$2.doInChunkContext(TaskletStep.java:273) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:82) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:375) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:145) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:258) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:208) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:152) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.AbstractJob.handleStep(AbstractJob.java:413) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.SimpleJob.doExecute(SimpleJob.java:136) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:320) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:149) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50) ~[spring-core-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:140) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
        at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:128) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at jdk.proxy2/jdk.proxy2.$Proxy83.run(Unknown Source) ~[na:na]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.execute(JobLauncherApplicationRunner.java:199) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.executeLocalJobs(JobLauncherApplicationRunner.java:173) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.launchJobFromProperties(JobLauncherApplicationRunner.java:160) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:155) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:150) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:762) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:752) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.littlewings.spring.batch.App.main(App.java:11) ~[classes!/:0.0.1-SNAPSHOT]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
        at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:108) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:58) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:65) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]

2022-05-27 01:29:24.471 ERROR 21982 --- [           main] o.s.batch.core.step.AbstractStep         : Encountered an error executing step chunkStep in job chunkJob

java.lang.RuntimeException: Oops!!
        at org.littlewings.spring.batch.BookItemProcessor.process(BookItemProcessor.java:13) ~[classes!/:0.0.1-SNAPSHOT]
        at org.littlewings.spring.batch.BookItemProcessor.process(BookItemProcessor.java:5) ~[classes!/:0.0.1-SNAPSHOT]
        at org.littlewings.spring.batch.BookItemProcessor$$FastClassBySpringCGLIB$$775ca5c7.invoke(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:137) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:124) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.littlewings.spring.batch.BookItemProcessor$$EnhancerBySpringCGLIB$$acf7a11d.process(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
        at org.springframework.batch.core.step.item.SimpleChunkProcessor.doProcess(SimpleChunkProcessor.java:134) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.SimpleChunkProcessor.transform(SimpleChunkProcessor.java:319) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.SimpleChunkProcessor.process(SimpleChunkProcessor.java:210) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.ChunkOrientedTasklet.execute(ChunkOrientedTasklet.java:77) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:407) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:331) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140) ~[spring-tx-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.step.tasklet.TaskletStep$2.doInChunkContext(TaskletStep.java:273) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:82) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:375) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:145) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:258) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:208) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:152) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.AbstractJob.handleStep(AbstractJob.java:413) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.SimpleJob.doExecute(SimpleJob.java:136) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:320) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:149) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50) ~[spring-core-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:140) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
        at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:128) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at jdk.proxy2/jdk.proxy2.$Proxy83.run(Unknown Source) ~[na:na]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.execute(JobLauncherApplicationRunner.java:199) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.executeLocalJobs(JobLauncherApplicationRunner.java:173) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.launchJobFromProperties(JobLauncherApplicationRunner.java:160) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:155) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:150) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:762) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:752) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.littlewings.spring.batch.App.main(App.java:11) ~[classes!/:0.0.1-SNAPSHOT]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
        at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:108) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:58) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:65) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]

2022-05-27 01:29:24.475  INFO 21982 --- [           main] o.s.batch.core.step.AbstractStep         : Step: [chunkStep] executed in 312ms
2022-05-27 01:29:24.502 DEBUG 21982 --- [           main] o.s.batch.core.step.AbstractStep         : Step execution complete: StepExecution: id=4, version=4, name=chunkStep, status=FAILED, exitStatus=FAILED, readCount=9, filterCount=0, writeCount=6 readSkipCount=0, writeSkipCount=0, processSkipCount=0, commitCount=2, rollbackCount=1
2022-05-27 01:29:24.534  INFO 21982 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=chunkJob]] completed with the following parameters: [{run.id=4}] and the following status: [FAILED] in 466ms

こちらもロールバックしているようです。

2022-05-27 01:29:24.461 DEBUG 21982 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Rollback for RuntimeException: java.lang.RuntimeException: Oops!!
2022-05-27 01:29:24.465 DEBUG 21982 --- [           main] o.s.t.support.TransactionTemplate        : Initiating transaction rollback on application exception

java.lang.RuntimeException: Oops!!
        at org.littlewings.spring.batch.BookItemProcessor.process(BookItemProcessor.java:13) ~[classes!/:0.0.1-SNAPSHOT]
        at org.littlewings.spring.batch.BookItemProcessor.process(BookItemProcessor.java:5) ~[classes!/:0.0.1-SNAPSHOT]
        at org.littlewings.spring.batch.BookItemProcessor$$FastClassBySpringCGLIB$$775ca5c7.invoke(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.20.jar!/:5.3.20]

データ。2つのチャンク分のデータまでが入っていますね。

mysql> select * from book;
+----------------+---------------------------------------------------------------------------------------------------------+-------+
| isbn           | title                                                                                                   | price |
+----------------+---------------------------------------------------------------------------------------------------------+-------+
| 978-1484237236 | The Definitive Guide to Spring Batch: Modern Finite Batch Processing in the Cloud                       |  7361 |
| 978-1492076988 | Spring Boot: Up and Running: Building Cloud Native Java and Kotlin Applications                         |  6265 |
| 978-4774182179 | [改訂新版]Spring入門 ――Javaフレームワーク・より良い設計とアーキテクチャ                                 |  4180 |
| 978-4797393118 | 基礎からのMySQL 第3版 (基礎からシリーズ)                                                                |  6038 |
| 978-4798142470 | Spring徹底入門 Spring FrameworkによるJavaアプリケーション開発                                           |  4400 |
| 978-4798161488 | MySQL徹底入門 第4版 MySQL 8.0対応                                                                       |  4180 |
+----------------+---------------------------------------------------------------------------------------------------------+-------+
6 rows in set (0.00 sec)

Exceptionをスローしてみます。

    @Override
    public Book process(Book item) throws Exception {
        currentCount++;

        if (currentCount > 7) {
            // throw new RuntimeException("Oops!!");
            throw new Exception("Oops!!");
        }

        return item;
    }

ログ。

2022-05-27 01:30:47.367  INFO 22148 --- [           main] o.s.b.a.b.JobLauncherApplicationRunner   : Running default command line with: []
2022-05-27 01:30:47.672  INFO 22148 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=chunkJob]] launched with the following parameters: [{run.id=5}]
2022-05-27 01:30:47.815  INFO 22148 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Executing step: [chunkStep]
2022-05-27 01:30:47.815 DEBUG 22148 --- [           main] o.s.batch.core.step.AbstractStep         : Executing: id=5
2022-05-27 01:30:48.011 DEBUG 22148 --- [           main] o.s.b.c.step.item.ChunkOrientedTasklet   : Inputs not busy, ended: false
2022-05-27 01:30:48.011 DEBUG 22148 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Applying contribution: [StepContribution: read=3, written=3, filtered=0, readSkips=0, writeSkips=0, processSkips=0, exitStatus=EXECUTING]
2022-05-27 01:30:48.014 DEBUG 22148 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Saving step execution before commit: StepExecution: id=5, version=1, name=chunkStep, status=STARTED, exitStatus=EXECUTING, readCount=3, filterCount=0, writeCount=3 readSkipCount=0, writeSkipCount=0, processSkipCount=0, commitCount=1, rollbackCount=0, exitDescription=
2022-05-27 01:30:48.049 DEBUG 22148 --- [           main] o.s.b.c.step.item.ChunkOrientedTasklet   : Inputs not busy, ended: false
2022-05-27 01:30:48.050 DEBUG 22148 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Applying contribution: [StepContribution: read=3, written=3, filtered=0, readSkips=0, writeSkips=0, processSkips=0, exitStatus=EXECUTING]
2022-05-27 01:30:48.052 DEBUG 22148 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Saving step execution before commit: StepExecution: id=5, version=2, name=chunkStep, status=STARTED, exitStatus=EXECUTING, readCount=6, filterCount=0, writeCount=6 readSkipCount=0, writeSkipCount=0, processSkipCount=0, commitCount=2, rollbackCount=0, exitDescription=
2022-05-27 01:30:48.083 DEBUG 22148 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Applying contribution: [StepContribution: read=3, written=0, filtered=0, readSkips=0, writeSkips=0, processSkips=0, exitStatus=EXECUTING]
2022-05-27 01:30:48.083 DEBUG 22148 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Rollback for Exception: java.lang.Exception: Oops!!
2022-05-27 01:30:48.088 DEBUG 22148 --- [           main] o.s.t.support.TransactionTemplate        : Initiating transaction rollback on application exception

org.springframework.batch.core.step.tasklet.UncheckedTransactionException: java.lang.Exception: Oops!!
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:487) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:331) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140) ~[spring-tx-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.step.tasklet.TaskletStep$2.doInChunkContext(TaskletStep.java:273) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:82) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:375) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:145) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:258) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:208) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:152) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.AbstractJob.handleStep(AbstractJob.java:413) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.SimpleJob.doExecute(SimpleJob.java:136) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:320) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:149) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50) ~[spring-core-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:140) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
        at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:128) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at jdk.proxy2/jdk.proxy2.$Proxy83.run(Unknown Source) ~[na:na]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.execute(JobLauncherApplicationRunner.java:199) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.executeLocalJobs(JobLauncherApplicationRunner.java:173) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.launchJobFromProperties(JobLauncherApplicationRunner.java:160) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:155) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:150) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:762) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:752) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.littlewings.spring.batch.App.main(App.java:11) ~[classes!/:0.0.1-SNAPSHOT]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
        at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:108) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:58) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:65) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
Caused by: java.lang.Exception: Oops!!
        at org.littlewings.spring.batch.BookItemProcessor.process(BookItemProcessor.java:14) ~[classes!/:0.0.1-SNAPSHOT]
        at org.littlewings.spring.batch.BookItemProcessor.process(BookItemProcessor.java:5) ~[classes!/:0.0.1-SNAPSHOT]
        at org.littlewings.spring.batch.BookItemProcessor$$FastClassBySpringCGLIB$$775ca5c7.invoke(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:137) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:124) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.littlewings.spring.batch.BookItemProcessor$$EnhancerBySpringCGLIB$$acf7a11d.process(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
        at org.springframework.batch.core.step.item.SimpleChunkProcessor.doProcess(SimpleChunkProcessor.java:134) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.SimpleChunkProcessor.transform(SimpleChunkProcessor.java:319) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.SimpleChunkProcessor.process(SimpleChunkProcessor.java:210) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.ChunkOrientedTasklet.execute(ChunkOrientedTasklet.java:77) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:407) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        ... 46 common frames omitted

2022-05-27 01:30:48.094 ERROR 22148 --- [           main] o.s.batch.core.step.AbstractStep         : Encountered an error executing step chunkStep in job chunkJob

java.lang.Exception: Oops!!
        at org.littlewings.spring.batch.BookItemProcessor.process(BookItemProcessor.java:14) ~[classes!/:0.0.1-SNAPSHOT]
        at org.littlewings.spring.batch.BookItemProcessor.process(BookItemProcessor.java:5) ~[classes!/:0.0.1-SNAPSHOT]
        at org.littlewings.spring.batch.BookItemProcessor$$FastClassBySpringCGLIB$$775ca5c7.invoke(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:137) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:124) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.littlewings.spring.batch.BookItemProcessor$$EnhancerBySpringCGLIB$$acf7a11d.process(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
        at org.springframework.batch.core.step.item.SimpleChunkProcessor.doProcess(SimpleChunkProcessor.java:134) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.SimpleChunkProcessor.transform(SimpleChunkProcessor.java:319) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.SimpleChunkProcessor.process(SimpleChunkProcessor.java:210) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.ChunkOrientedTasklet.execute(ChunkOrientedTasklet.java:77) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:407) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:331) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140) ~[spring-tx-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.step.tasklet.TaskletStep$2.doInChunkContext(TaskletStep.java:273) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:82) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:375) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:145) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:258) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:208) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:152) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.AbstractJob.handleStep(AbstractJob.java:413) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.SimpleJob.doExecute(SimpleJob.java:136) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:320) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:149) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50) ~[spring-core-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:140) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
        at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:128) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at jdk.proxy2/jdk.proxy2.$Proxy83.run(Unknown Source) ~[na:na]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.execute(JobLauncherApplicationRunner.java:199) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.executeLocalJobs(JobLauncherApplicationRunner.java:173) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.launchJobFromProperties(JobLauncherApplicationRunner.java:160) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:155) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:150) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:762) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:752) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.littlewings.spring.batch.App.main(App.java:11) ~[classes!/:0.0.1-SNAPSHOT]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
        at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:108) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:58) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:65) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]

2022-05-27 01:30:48.100  INFO 22148 --- [           main] o.s.batch.core.step.AbstractStep         : Step: [chunkStep] executed in 284ms
2022-05-27 01:30:48.154 DEBUG 22148 --- [           main] o.s.batch.core.step.AbstractStep         : Step execution complete: StepExecution: id=5, version=4, name=chunkStep, status=FAILED, exitStatus=FAILED, readCount=9, filterCount=0, writeCount=6 readSkipCount=0, writeSkipCount=0, processSkipCount=0, commitCount=2, rollbackCount=1
2022-05-27 01:30:48.205  INFO 22148 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=chunkJob]] completed with the following parameters: [{run.id=5}] and the following status: [FAILED] in 455ms

ロールバックされたようです。

データ。

mysql> select * from book;
+----------------+---------------------------------------------------------------------------------------------------------+-------+
| isbn           | title                                                                                                   | price |
+----------------+---------------------------------------------------------------------------------------------------------+-------+
| 978-1484237236 | The Definitive Guide to Spring Batch: Modern Finite Batch Processing in the Cloud                       |  7361 |
| 978-1492076988 | Spring Boot: Up and Running: Building Cloud Native Java and Kotlin Applications                         |  6265 |
| 978-4774182179 | [改訂新版]Spring入門 ――Javaフレームワーク・より良い設計とアーキテクチャ                                 |  4180 |
| 978-4797393118 | 基礎からのMySQL 第3版 (基礎からシリーズ)                                                                |  6038 |
| 978-4798142470 | Spring徹底入門 Spring FrameworkによるJavaアプリケーション開発                                           |  4400 |
| 978-4798161488 | MySQL徹底入門 第4版 MySQL 8.0対応                                                                       |  4180 |
+----------------+---------------------------------------------------------------------------------------------------------+-------+
6 rows in set (0.00 sec)

両方ともロールバックしましたが、やっぱりスタックトレースが変わりましたね。

RuntimeExceptionの場合。

2022-05-27 01:29:24.461 DEBUG 21982 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Rollback for RuntimeException: java.lang.RuntimeException: Oops!!
2022-05-27 01:29:24.465 DEBUG 21982 --- [           main] o.s.t.support.TransactionTemplate        : Initiating transaction rollback on application exception

java.lang.RuntimeException: Oops!!
        at org.littlewings.spring.batch.BookItemProcessor.process(BookItemProcessor.java:13) ~[classes!/:0.0.1-SNAPSHOT]
        at org.littlewings.spring.batch.BookItemProcessor.process(BookItemProcessor.java:5) ~[classes!/:0.0.1-SNAPSHOT]
        at org.littlewings.spring.batch.BookItemProcessor$$FastClassBySpringCGLIB$$775ca5c7.invoke(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.20.jar!/:5.3.20]

〜省略〜

Exceptionの場合。

2022-05-27 01:30:48.083 DEBUG 22148 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Rollback for Exception: java.lang.Exception: Oops!!
2022-05-27 01:30:48.088 DEBUG 22148 --- [           main] o.s.t.support.TransactionTemplate        : Initiating transaction rollback on application exception

org.springframework.batch.core.step.tasklet.UncheckedTransactionException: java.lang.Exception: Oops!!
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:487) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:331) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140) ~[spring-tx-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.step.tasklet.TaskletStep$2.doInChunkContext(TaskletStep.java:273) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:82) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:375) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:145) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:258) ~[spring-batch-core-4.3.6.jar!/:4.3.6]

〜省略〜


Caused by: java.lang.Exception: Oops!!
        at org.littlewings.spring.batch.BookItemProcessor.process(BookItemProcessor.java:14) ~[classes!/:0.0.1-SNAPSHOT]
        at org.littlewings.spring.batch.BookItemProcessor.process(BookItemProcessor.java:5) ~[classes!/:0.0.1-SNAPSHOT]
        at org.littlewings.spring.batch.BookItemProcessor$$FastClassBySpringCGLIB$$775ca5c7.invoke(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.20.jar!/:5.3.20]

〜省略〜

ItemProcessorの場合は、Exceptionのメッセージが変わっており、Exceptionによりロールバックしたことになっています。

2022-05-27 01:30:48.083 DEBUG 22148 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Rollback for Exception: java.lang.Exception: Oops!!
2022-05-27 01:30:48.088 DEBUG 22148 --- [           main] o.s.t.support.TransactionTemplate        : Initiating transaction rollback on application exception

org.springframework.batch.core.step.tasklet.UncheckedTransactionException: java.lang.Exception: Oops!!
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:487) ~[spring-batch-core-4.3.6.jar!/:4.3.6]

そして、UncheckedTransactionExceptionをスローしたことになっています。

ItemProcessorが例外をする場合でも、RuntimeExceptionExceptionの両方でロールバックすることがわかりました。

ItemProcessorは、元に戻しておきます。

    @Override
    public Book process(Book item) throws Exception {
        currentCount++;

        if (currentCount > 7) {
            // throw new RuntimeException("Oops!!");
            // throw new Exception("Oops!!");
        }

        return item;
    }
ItemWriterが例外をスローする場合

チャンクの最後は、ItemWriterです。

RuntimeExceptionをスローします。

    @Override
    public void write(List<? extends Book> items) throws Exception {
        for (Book book : items) {
            currentCount++;

            if (currentCount > 7) {
                throw new RuntimeException("Oops!!");
                // throw new Exception("Oops!!");
            }

            entityManager.persist(book);
            entityManager.flush();
        }
    }

ログ。

2022-05-27 01:34:44.319  INFO 22497 --- [           main] o.s.b.a.b.JobLauncherApplicationRunner   : Running default command line with: []
2022-05-27 01:34:44.666  INFO 22497 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=chunkJob]] launched with the following parameters: [{run.id=6}]
2022-05-27 01:34:44.782  INFO 22497 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Executing step: [chunkStep]
2022-05-27 01:34:44.782 DEBUG 22497 --- [           main] o.s.batch.core.step.AbstractStep         : Executing: id=6
2022-05-27 01:34:44.987 DEBUG 22497 --- [           main] o.s.b.c.step.item.ChunkOrientedTasklet   : Inputs not busy, ended: false
2022-05-27 01:34:44.988 DEBUG 22497 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Applying contribution: [StepContribution: read=3, written=3, filtered=0, readSkips=0, writeSkips=0, processSkips=0, exitStatus=EXECUTING]
2022-05-27 01:34:44.991 DEBUG 22497 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Saving step execution before commit: StepExecution: id=6, version=1, name=chunkStep, status=STARTED, exitStatus=EXECUTING, readCount=3, filterCount=0, writeCount=3 readSkipCount=0, writeSkipCount=0, processSkipCount=0, commitCount=1, rollbackCount=0, exitDescription=
2022-05-27 01:34:45.031 DEBUG 22497 --- [           main] o.s.b.c.step.item.ChunkOrientedTasklet   : Inputs not busy, ended: false
2022-05-27 01:34:45.032 DEBUG 22497 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Applying contribution: [StepContribution: read=3, written=3, filtered=0, readSkips=0, writeSkips=0, processSkips=0, exitStatus=EXECUTING]
2022-05-27 01:34:45.034 DEBUG 22497 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Saving step execution before commit: StepExecution: id=6, version=2, name=chunkStep, status=STARTED, exitStatus=EXECUTING, readCount=6, filterCount=0, writeCount=6 readSkipCount=0, writeSkipCount=0, processSkipCount=0, commitCount=2, rollbackCount=0, exitDescription=
2022-05-27 01:34:45.064 DEBUG 22497 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Applying contribution: [StepContribution: read=3, written=0, filtered=0, readSkips=0, writeSkips=0, processSkips=0, exitStatus=EXECUTING]
2022-05-27 01:34:45.064 DEBUG 22497 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Rollback for RuntimeException: java.lang.RuntimeException: Oops!!
2022-05-27 01:34:45.068 DEBUG 22497 --- [           main] o.s.t.support.TransactionTemplate        : Initiating transaction rollback on application exception

java.lang.RuntimeException: Oops!!
        at org.littlewings.spring.batch.BookItemWriter.write(BookItemWriter.java:22) ~[classes!/:0.0.1-SNAPSHOT]
        at org.littlewings.spring.batch.BookItemWriter$$FastClassBySpringCGLIB$$bd3df5e.invoke(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:137) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:124) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.littlewings.spring.batch.BookItemWriter$$EnhancerBySpringCGLIB$$37836e73.write(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
        at org.springframework.batch.core.step.item.SimpleChunkProcessor.writeItems(SimpleChunkProcessor.java:193) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.SimpleChunkProcessor.doWrite(SimpleChunkProcessor.java:159) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.SimpleChunkProcessor.write(SimpleChunkProcessor.java:294) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.SimpleChunkProcessor.process(SimpleChunkProcessor.java:217) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.ChunkOrientedTasklet.execute(ChunkOrientedTasklet.java:77) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:407) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:331) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140) ~[spring-tx-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.step.tasklet.TaskletStep$2.doInChunkContext(TaskletStep.java:273) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:82) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:375) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:145) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:258) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:208) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:152) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.AbstractJob.handleStep(AbstractJob.java:413) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.SimpleJob.doExecute(SimpleJob.java:136) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:320) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:149) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50) ~[spring-core-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:140) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
        at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:128) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at jdk.proxy2/jdk.proxy2.$Proxy83.run(Unknown Source) ~[na:na]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.execute(JobLauncherApplicationRunner.java:199) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.executeLocalJobs(JobLauncherApplicationRunner.java:173) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.launchJobFromProperties(JobLauncherApplicationRunner.java:160) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:155) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:150) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:762) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:752) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.littlewings.spring.batch.App.main(App.java:11) ~[classes!/:0.0.1-SNAPSHOT]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
        at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:108) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:58) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:65) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]

2022-05-27 01:34:45.113 ERROR 22497 --- [           main] o.s.batch.core.step.AbstractStep         : Encountered an error executing step chunkStep in job chunkJob

java.lang.RuntimeException: Oops!!
        at org.littlewings.spring.batch.BookItemWriter.write(BookItemWriter.java:22) ~[classes!/:0.0.1-SNAPSHOT]
        at org.littlewings.spring.batch.BookItemWriter$$FastClassBySpringCGLIB$$bd3df5e.invoke(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:137) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:124) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.littlewings.spring.batch.BookItemWriter$$EnhancerBySpringCGLIB$$37836e73.write(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
        at org.springframework.batch.core.step.item.SimpleChunkProcessor.writeItems(SimpleChunkProcessor.java:193) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.SimpleChunkProcessor.doWrite(SimpleChunkProcessor.java:159) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.SimpleChunkProcessor.write(SimpleChunkProcessor.java:294) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.SimpleChunkProcessor.process(SimpleChunkProcessor.java:217) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.ChunkOrientedTasklet.execute(ChunkOrientedTasklet.java:77) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:407) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:331) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140) ~[spring-tx-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.step.tasklet.TaskletStep$2.doInChunkContext(TaskletStep.java:273) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:82) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:375) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:145) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:258) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:208) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:152) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.AbstractJob.handleStep(AbstractJob.java:413) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.SimpleJob.doExecute(SimpleJob.java:136) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:320) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:149) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50) ~[spring-core-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:140) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
        at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:128) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at jdk.proxy2/jdk.proxy2.$Proxy83.run(Unknown Source) ~[na:na]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.execute(JobLauncherApplicationRunner.java:199) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.executeLocalJobs(JobLauncherApplicationRunner.java:173) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.launchJobFromProperties(JobLauncherApplicationRunner.java:160) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:155) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:150) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:762) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:752) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.littlewings.spring.batch.App.main(App.java:11) ~[classes!/:0.0.1-SNAPSHOT]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
        at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:108) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:58) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:65) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]

2022-05-27 01:34:45.118  INFO 22497 --- [           main] o.s.batch.core.step.AbstractStep         : Step: [chunkStep] executed in 334ms
2022-05-27 01:34:45.164 DEBUG 22497 --- [           main] o.s.batch.core.step.AbstractStep         : Step execution complete: StepExecution: id=6, version=4, name=chunkStep, status=FAILED, exitStatus=FAILED, readCount=9, filterCount=0, writeCount=6 readSkipCount=0, writeSkipCount=0, processSkipCount=0, commitCount=2, rollbackCount=1
2022-05-27 01:34:45.274  INFO 22497 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=chunkJob]] completed with the following parameters: [{run.id=6}] and the following status: [FAILED] in 470ms

ロールバックしています。

2022-05-27 01:34:45.064 DEBUG 22497 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Rollback for RuntimeException: java.lang.RuntimeException: Oops!!
2022-05-27 01:34:45.068 DEBUG 22497 --- [           main] o.s.t.support.TransactionTemplate        : Initiating transaction rollback on application exception

java.lang.RuntimeException: Oops!!
        at org.littlewings.spring.batch.BookItemWriter.write(BookItemWriter.java:22) ~[classes!/:0.0.1-SNAPSHOT]
        at org.littlewings.spring.batch.BookItemWriter$$FastClassBySpringCGLIB$$bd3df5e.invoke(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.20.jar!/:5.3.20]

〜省略〜

データ。やはり、チャンク2つ分です。

mysql> select * from book;
+----------------+---------------------------------------------------------------------------------------------------------+-------+
| isbn           | title                                                                                                   | price |
+----------------+---------------------------------------------------------------------------------------------------------+-------+
| 978-1484237236 | The Definitive Guide to Spring Batch: Modern Finite Batch Processing in the Cloud                       |  7361 |
| 978-1492076988 | Spring Boot: Up and Running: Building Cloud Native Java and Kotlin Applications                         |  6265 |
| 978-4774182179 | [改訂新版]Spring入門 ――Javaフレームワーク・より良い設計とアーキテクチャ                                 |  4180 |
| 978-4797393118 | 基礎からのMySQL 第3版 (基礎からシリーズ)                                                                |  6038 |
| 978-4798142470 | Spring徹底入門 Spring FrameworkによるJavaアプリケーション開発                                           |  4400 |
| 978-4798161488 | MySQL徹底入門 第4版 MySQL 8.0対応                                                                       |  4180 |
+----------------+---------------------------------------------------------------------------------------------------------+-------+
6 rows in set (0.00 sec)

Exceptionをスローしてみます。

    @Override
    public void write(List<? extends Book> items) throws Exception {
        for (Book book : items) {
            currentCount++;

            if (currentCount > 7) {
                // throw new RuntimeException("Oops!!");
                throw new Exception("Oops!!");
            }

            entityManager.persist(book);
            entityManager.flush();
        }
    }

ログ。

2022-05-27 01:36:02.283  INFO 22698 --- [           main] o.s.b.a.b.JobLauncherApplicationRunner   : Running default command line with: []
2022-05-27 01:36:02.598  INFO 22698 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=chunkJob]] launched with the following parameters: [{run.id=7}]
2022-05-27 01:36:02.715  INFO 22698 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Executing step: [chunkStep]
2022-05-27 01:36:02.715 DEBUG 22698 --- [           main] o.s.batch.core.step.AbstractStep         : Executing: id=7
2022-05-27 01:36:02.920 DEBUG 22698 --- [           main] o.s.b.c.step.item.ChunkOrientedTasklet   : Inputs not busy, ended: false
2022-05-27 01:36:02.920 DEBUG 22698 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Applying contribution: [StepContribution: read=3, written=3, filtered=0, readSkips=0, writeSkips=0, processSkips=0, exitStatus=EXECUTING]
2022-05-27 01:36:02.923 DEBUG 22698 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Saving step execution before commit: StepExecution: id=7, version=1, name=chunkStep, status=STARTED, exitStatus=EXECUTING, readCount=3, filterCount=0, writeCount=3 readSkipCount=0, writeSkipCount=0, processSkipCount=0, commitCount=1, rollbackCount=0, exitDescription=
2022-05-27 01:36:02.958 DEBUG 22698 --- [           main] o.s.b.c.step.item.ChunkOrientedTasklet   : Inputs not busy, ended: false
2022-05-27 01:36:02.958 DEBUG 22698 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Applying contribution: [StepContribution: read=3, written=3, filtered=0, readSkips=0, writeSkips=0, processSkips=0, exitStatus=EXECUTING]
2022-05-27 01:36:02.960 DEBUG 22698 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Saving step execution before commit: StepExecution: id=7, version=2, name=chunkStep, status=STARTED, exitStatus=EXECUTING, readCount=6, filterCount=0, writeCount=6 readSkipCount=0, writeSkipCount=0, processSkipCount=0, commitCount=2, rollbackCount=0, exitDescription=
2022-05-27 01:36:02.990 DEBUG 22698 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Applying contribution: [StepContribution: read=3, written=0, filtered=0, readSkips=0, writeSkips=0, processSkips=0, exitStatus=EXECUTING]
2022-05-27 01:36:02.990 DEBUG 22698 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Rollback for Exception: java.lang.Exception: Oops!!
2022-05-27 01:36:02.995 DEBUG 22698 --- [           main] o.s.t.support.TransactionTemplate        : Initiating transaction rollback on application exception

org.springframework.batch.core.step.tasklet.UncheckedTransactionException: java.lang.Exception: Oops!!
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:487) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:331) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140) ~[spring-tx-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.step.tasklet.TaskletStep$2.doInChunkContext(TaskletStep.java:273) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:82) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:375) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:145) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:258) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:208) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:152) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.AbstractJob.handleStep(AbstractJob.java:413) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.SimpleJob.doExecute(SimpleJob.java:136) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:320) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:149) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50) ~[spring-core-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:140) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
        at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:128) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at jdk.proxy2/jdk.proxy2.$Proxy83.run(Unknown Source) ~[na:na]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.execute(JobLauncherApplicationRunner.java:199) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.executeLocalJobs(JobLauncherApplicationRunner.java:173) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.launchJobFromProperties(JobLauncherApplicationRunner.java:160) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:155) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:150) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:762) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:752) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.littlewings.spring.batch.App.main(App.java:11) ~[classes!/:0.0.1-SNAPSHOT]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
        at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:108) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:58) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:65) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
Caused by: java.lang.Exception: Oops!!
        at org.littlewings.spring.batch.BookItemWriter.write(BookItemWriter.java:23) ~[classes!/:0.0.1-SNAPSHOT]
        at org.littlewings.spring.batch.BookItemWriter$$FastClassBySpringCGLIB$$bd3df5e.invoke(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:137) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:124) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.littlewings.spring.batch.BookItemWriter$$EnhancerBySpringCGLIB$$e1ee2674.write(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
        at org.springframework.batch.core.step.item.SimpleChunkProcessor.writeItems(SimpleChunkProcessor.java:193) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.SimpleChunkProcessor.doWrite(SimpleChunkProcessor.java:159) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.SimpleChunkProcessor.write(SimpleChunkProcessor.java:294) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.SimpleChunkProcessor.process(SimpleChunkProcessor.java:217) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.ChunkOrientedTasklet.execute(ChunkOrientedTasklet.java:77) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:407) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        ... 46 common frames omitted

2022-05-27 01:36:03.011 ERROR 22698 --- [           main] o.s.batch.core.step.AbstractStep         : Encountered an error executing step chunkStep in job chunkJob

java.lang.Exception: Oops!!
        at org.littlewings.spring.batch.BookItemWriter.write(BookItemWriter.java:23) ~[classes!/:0.0.1-SNAPSHOT]
        at org.littlewings.spring.batch.BookItemWriter$$FastClassBySpringCGLIB$$bd3df5e.invoke(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:137) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:124) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.littlewings.spring.batch.BookItemWriter$$EnhancerBySpringCGLIB$$e1ee2674.write(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
        at org.springframework.batch.core.step.item.SimpleChunkProcessor.writeItems(SimpleChunkProcessor.java:193) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.SimpleChunkProcessor.doWrite(SimpleChunkProcessor.java:159) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.SimpleChunkProcessor.write(SimpleChunkProcessor.java:294) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.SimpleChunkProcessor.process(SimpleChunkProcessor.java:217) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.item.ChunkOrientedTasklet.execute(ChunkOrientedTasklet.java:77) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:407) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:331) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140) ~[spring-tx-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.step.tasklet.TaskletStep$2.doInChunkContext(TaskletStep.java:273) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:82) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:375) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:145) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:258) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:208) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:152) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.AbstractJob.handleStep(AbstractJob.java:413) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.SimpleJob.doExecute(SimpleJob.java:136) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:320) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:149) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50) ~[spring-core-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:140) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
        at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:128) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at jdk.proxy2/jdk.proxy2.$Proxy83.run(Unknown Source) ~[na:na]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.execute(JobLauncherApplicationRunner.java:199) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.executeLocalJobs(JobLauncherApplicationRunner.java:173) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.launchJobFromProperties(JobLauncherApplicationRunner.java:160) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:155) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:150) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:762) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:752) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.littlewings.spring.batch.App.main(App.java:11) ~[classes!/:0.0.1-SNAPSHOT]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
        at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:108) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:58) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:65) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]

2022-05-27 01:36:03.014  INFO 22698 --- [           main] o.s.batch.core.step.AbstractStep         : Step: [chunkStep] executed in 299ms
2022-05-27 01:36:03.068 DEBUG 22698 --- [           main] o.s.batch.core.step.AbstractStep         : Step execution complete: StepExecution: id=7, version=4, name=chunkStep, status=FAILED, exitStatus=FAILED, readCount=9, filterCount=0, writeCount=6 readSkipCount=0, writeSkipCount=0, processSkipCount=0, commitCount=2, rollbackCount=1
2022-05-27 01:36:03.102  INFO 22698 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=chunkJob]] completed with the following parameters: [{run.id=7}] and the following status: [FAILED] in 448ms

ロールバックしたようです。

データ。

mysql> select * from book;
+----------------+---------------------------------------------------------------------------------------------------------+-------+
| isbn           | title                                                                                                   | price |
+----------------+---------------------------------------------------------------------------------------------------------+-------+
| 978-1484237236 | The Definitive Guide to Spring Batch: Modern Finite Batch Processing in the Cloud                       |  7361 |
| 978-1492076988 | Spring Boot: Up and Running: Building Cloud Native Java and Kotlin Applications                         |  6265 |
| 978-4774182179 | [改訂新版]Spring入門 ――Javaフレームワーク・より良い設計とアーキテクチャ                                 |  4180 |
| 978-4797393118 | 基礎からのMySQL 第3版 (基礎からシリーズ)                                                                |  6038 |
| 978-4798142470 | Spring徹底入門 Spring FrameworkによるJavaアプリケーション開発                                           |  4400 |
| 978-4798161488 | MySQL徹底入門 第4版 MySQL 8.0対応                                                                       |  4180 |
+----------------+---------------------------------------------------------------------------------------------------------+-------+
6 rows in set (0.00 sec)

ItemWriterの場合は、ItemProcessorと同様にExceptionとしてロールバックしているようです。

2022-05-27 01:36:02.990 DEBUG 22698 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Rollback for Exception: java.lang.Exception: Oops!!
2022-05-27 01:36:02.995 DEBUG 22698 --- [           main] o.s.t.support.TransactionTemplate        : Initiating transaction rollback on application exception

org.springframework.batch.core.step.tasklet.UncheckedTransactionException: java.lang.Exception: Oops!!
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:487) ~[spring-batch-core-4.3.6.jar!/:4.3.6]

〜省略〜

UncheckedTransactionExceptionにラップされているのも同じようですが。

これで、ItemWriterの場合もスローされる例外がExceptionRuntimeExceptionを問わずロールバックされることがわかりました。

ItemWriterソースコードは、元に戻しておきます。

    @Override
    public void write(List<? extends Book> items) throws Exception {
        for (Book book : items) {
            currentCount++;

            if (currentCount > 7) {
                // throw new RuntimeException("Oops!!");
                // throw new Exception("Oops!!");
            }

            entityManager.persist(book);
            entityManager.flush();
        }
    }

Tasklet

チャンクの次は、Taskletを見ていきます。

Taskletソースコードは、こちら。

src/main/java/org/littlewings/spring/batch/BookTasklet.java

package org.littlewings.spring.batch;

import java.util.List;
import javax.persistence.EntityManager;

import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.beans.factory.annotation.Autowired;

public class BookTasklet implements Tasklet {
    @Autowired
    EntityManager entityManager;

    @Override
    public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
        List<Book> books = List.of(
                Book.create("978-4798142470", "Spring徹底入門 Spring FrameworkによるJavaアプリケーション開発", 4400),
                Book.create("978-4774182179", "[改訂新版]Spring入門 ――Javaフレームワーク・より良い設計とアーキテクチャ", 4180),
                Book.create("978-1492076988", "Spring Boot: Up and Running: Building Cloud Native Java and Kotlin Applications", 6265),
                Book.create("978-1484237236", "The Definitive Guide to Spring Batch: Modern Finite Batch Processing in the Cloud", 7361),
                Book.create("978-4798161488", "MySQL徹底入門 第4版 MySQL 8.0対応", 4180),
                Book.create("978-4797393118", "基礎からのMySQL 第3版 (基礎からシリーズ)", 6038),
                Book.create("978-4873116389", "実践ハイパフォーマンスMySQL 第3版", 5280),
                Book.create("978-4295000198", "やさしく学べるMySQL運用・管理入門【5.7対応】", 2860),
                Book.create("978-4798147406", "詳解MySQL 5.7 止まらぬ進化に乗り遅れないためのテクニカルガイド (NEXT ONE)", 3960),
                Book.create("978-4774170206", "MariaDB&MySQL全機能バイブル", 3860)
        );

        int currentCount = 0;

        for (Book book : books) {
            currentCount++;

            if (currentCount > 7) {
                // throw new RuntimeException("Oops!!");
                // throw new Exception("Oops!!");
            }

            entityManager.persist(book);
            entityManager.flush();
        }

        return RepeatStatus.FINISHED;
    }
}

10件の書籍データを、1件ずつ反映します。

Taskletもチャンクの時と同様、7件目のデータを扱うところでコメントアウトを解除すると例外をスローするようになっています。
RuntimeExceptionおよびExceptionです。

            if (currentCount > 7) {
                // throw new RuntimeException("Oops!!");
                // throw new Exception("Oops!!");
            }

こちらもこのコメントアウトの解除を変化させつつ、どのような動きをするかを見ていきます。

Jobの定義はこちら。

src/main/java/org/littlewings/spring/batch/TaskletJobConfig.java

package org.littlewings.spring.batch;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepScope;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class TaskletJobConfig {
    @Bean
    public Job taskletJob(JobBuilderFactory jobBuilderFactory) {
        return jobBuilderFactory
                .get("taskletJob")
                .incrementer(new RunIdIncrementer())
                .start(taskletStep(null))
                .build();
    }

    @Bean
    public Step taskletStep(StepBuilderFactory stepBuilderFactory) {
        return stepBuilderFactory
                .get("taskletStep")
                .tasklet(bookTasklet())
                .build();
    }

    @Bean
    @StepScope
    public BookTasklet bookTasklet() {
        return new BookTasklet();
    }
}

確認は、チャンクと同じようにソースコードを変更しつつパッケージングして

$ mvn package

Jobを指定して実行することにします。

$ java -Dspring.batch.job.names=taskletJob -jar target/batch-transaction-0.0.1-SNAPSHOT.jar
正常に終了する場合

まずは、正常に終了する場合から。

実行時のログは、こちら。

2022-05-27 01:37:47.923  INFO 22877 --- [           main] o.s.b.a.b.JobLauncherApplicationRunner   : Running default command line with: []
2022-05-27 01:37:48.192  INFO 22877 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=taskletJob]] launched with the following parameters: [{run.id=1}]
2022-05-27 01:37:48.373  INFO 22877 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Executing step: [taskletStep]
2022-05-27 01:37:48.373 DEBUG 22877 --- [           main] o.s.batch.core.step.AbstractStep         : Executing: id=8
2022-05-27 01:37:48.543 DEBUG 22877 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Applying contribution: [StepContribution: read=0, written=0, filtered=0, readSkips=0, writeSkips=0, processSkips=0, exitStatus=EXECUTING]
2022-05-27 01:37:48.546 DEBUG 22877 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Saving step execution before commit: StepExecution: id=8, version=1, name=taskletStep, status=STARTED, exitStatus=EXECUTING, readCount=0, filterCount=0, writeCount=0 readSkipCount=0, writeSkipCount=0, processSkipCount=0, commitCount=1, rollbackCount=0, exitDescription=
2022-05-27 01:37:48.573 DEBUG 22877 --- [           main] o.s.batch.core.step.AbstractStep         : Step execution success: id=8
2022-05-27 01:37:48.582  INFO 22877 --- [           main] o.s.batch.core.step.AbstractStep         : Step: [taskletStep] executed in 208ms
2022-05-27 01:37:48.609 DEBUG 22877 --- [           main] o.s.batch.core.step.AbstractStep         : Step execution complete: StepExecution: id=8, version=3, name=taskletStep, status=COMPLETED, exitStatus=COMPLETED, readCount=0, filterCount=0, writeCount=0 readSkipCount=0, writeSkipCount=0, processSkipCount=0, commitCount=1, rollbackCount=0
2022-05-27 01:37:48.637  INFO 22877 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=taskletJob]] completed with the following parameters: [{run.id=1}] and the following status: [COMPLETED] in 390ms

データ。

mysql> select * from book;
+----------------+---------------------------------------------------------------------------------------------------------+-------+
| isbn           | title                                                                                                   | price |
+----------------+---------------------------------------------------------------------------------------------------------+-------+
| 978-1484237236 | The Definitive Guide to Spring Batch: Modern Finite Batch Processing in the Cloud                       |  7361 |
| 978-1492076988 | Spring Boot: Up and Running: Building Cloud Native Java and Kotlin Applications                         |  6265 |
| 978-4295000198 | やさしく学べるMySQL運用・管理入門【5.7対応】                                                            |  2860 |
| 978-4774170206 | MariaDB&MySQL全機能バイブル                                                                             |  3860 |
| 978-4774182179 | [改訂新版]Spring入門 ――Javaフレームワーク・より良い設計とアーキテクチャ                                 |  4180 |
| 978-4797393118 | 基礎からのMySQL 第3版 (基礎からシリーズ)                                                                |  6038 |
| 978-4798142470 | Spring徹底入門 Spring FrameworkによるJavaアプリケーション開発                                           |  4400 |
| 978-4798147406 | 詳解MySQL 5.7 止まらぬ進化に乗り遅れないためのテクニカルガイド (NEXT ONE)                               |  3960 |
| 978-4798161488 | MySQL徹底入門 第4版 MySQL 8.0対応                                                                       |  4180 |
| 978-4873116389 | 実践ハイパフォーマンスMySQL 第3版                                                                       |  5280 |
+----------------+---------------------------------------------------------------------------------------------------------+-------+
10 rows in set (0.00 sec)
Taskletが例外をスローする場合

では、例外をスローするようにして動作確認してみます。

まずはRuntimeExceptionをスローしてみます。

        for (Book book : books) {
            currentCount++;

            if (currentCount > 7) {
                throw new RuntimeException("Oops!!");
                // throw new Exception("Oops!!");
            }

            entityManager.persist(book);
            entityManager.flush();
        }

ログ。

2022-05-27 01:39:52.695  INFO 23081 --- [           main] o.s.b.a.b.JobLauncherApplicationRunner   : Running default command line with: []
2022-05-27 01:39:53.006  INFO 23081 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=taskletJob]] launched with the following parameters: [{run.id=2}]
2022-05-27 01:39:53.140  INFO 23081 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Executing step: [taskletStep]
2022-05-27 01:39:53.140 DEBUG 23081 --- [           main] o.s.batch.core.step.AbstractStep         : Executing: id=9
2022-05-27 01:39:53.320 DEBUG 23081 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Applying contribution: [StepContribution: read=0, written=0, filtered=0, readSkips=0, writeSkips=0, processSkips=0, exitStatus=EXECUTING]
2022-05-27 01:39:53.320 DEBUG 23081 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Rollback for RuntimeException: java.lang.RuntimeException: Oops!!
2022-05-27 01:39:53.326 DEBUG 23081 --- [           main] o.s.t.support.TransactionTemplate        : Initiating transaction rollback on application exception

java.lang.RuntimeException: Oops!!
        at org.littlewings.spring.batch.BookTasklet.execute(BookTasklet.java:37) ~[classes!/:0.0.1-SNAPSHOT]
        at org.littlewings.spring.batch.BookTasklet$$FastClassBySpringCGLIB$$1150977e.invoke(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:137) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:124) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.littlewings.spring.batch.BookTasklet$$EnhancerBySpringCGLIB$$54aa2694.execute(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:407) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:331) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140) ~[spring-tx-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.step.tasklet.TaskletStep$2.doInChunkContext(TaskletStep.java:273) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:82) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:375) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:145) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:258) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:208) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:152) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.AbstractJob.handleStep(AbstractJob.java:413) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.SimpleJob.doExecute(SimpleJob.java:136) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:320) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:149) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50) ~[spring-core-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:140) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
        at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:128) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at jdk.proxy2/jdk.proxy2.$Proxy83.run(Unknown Source) ~[na:na]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.execute(JobLauncherApplicationRunner.java:199) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.executeLocalJobs(JobLauncherApplicationRunner.java:173) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.launchJobFromProperties(JobLauncherApplicationRunner.java:160) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:155) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:150) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:762) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:752) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.littlewings.spring.batch.App.main(App.java:11) ~[classes!/:0.0.1-SNAPSHOT]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
        at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:108) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:58) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:65) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]

2022-05-27 01:39:53.363 ERROR 23081 --- [           main] o.s.batch.core.step.AbstractStep         : Encountered an error executing step taskletStep in job taskletJob

java.lang.RuntimeException: Oops!!
        at org.littlewings.spring.batch.BookTasklet.execute(BookTasklet.java:37) ~[classes!/:0.0.1-SNAPSHOT]
        at org.littlewings.spring.batch.BookTasklet$$FastClassBySpringCGLIB$$1150977e.invoke(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:137) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:124) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.littlewings.spring.batch.BookTasklet$$EnhancerBySpringCGLIB$$54aa2694.execute(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:407) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:331) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140) ~[spring-tx-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.step.tasklet.TaskletStep$2.doInChunkContext(TaskletStep.java:273) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:82) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:375) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:145) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:258) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:208) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:152) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.AbstractJob.handleStep(AbstractJob.java:413) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.SimpleJob.doExecute(SimpleJob.java:136) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:320) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:149) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50) ~[spring-core-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:140) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
        at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:128) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at jdk.proxy2/jdk.proxy2.$Proxy83.run(Unknown Source) ~[na:na]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.execute(JobLauncherApplicationRunner.java:199) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.executeLocalJobs(JobLauncherApplicationRunner.java:173) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.launchJobFromProperties(JobLauncherApplicationRunner.java:160) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:155) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:150) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:762) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:752) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.littlewings.spring.batch.App.main(App.java:11) ~[classes!/:0.0.1-SNAPSHOT]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
        at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:108) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:58) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:65) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]

2022-05-27 01:39:53.369  INFO 23081 --- [           main] o.s.batch.core.step.AbstractStep         : Step: [taskletStep] executed in 229ms
2022-05-27 01:39:53.410 DEBUG 23081 --- [           main] o.s.batch.core.step.AbstractStep         : Step execution complete: StepExecution: id=9, version=2, name=taskletStep, status=FAILED, exitStatus=FAILED, readCount=0, filterCount=0, writeCount=0 readSkipCount=0, writeSkipCount=0, processSkipCount=0, commitCount=0, rollbackCount=1
2022-05-27 01:39:53.441  INFO 23081 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=taskletJob]] completed with the following parameters: [{run.id=2}] and the following status: [FAILED] in 384ms

ロールバックしています。

2022-05-27 01:39:53.320 DEBUG 23081 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Rollback for RuntimeException: java.lang.RuntimeException: Oops!!
2022-05-27 01:39:53.326 DEBUG 23081 --- [           main] o.s.t.support.TransactionTemplate        : Initiating transaction rollback on application exception

java.lang.RuntimeException: Oops!!
        at org.littlewings.spring.batch.BookTasklet.execute(BookTasklet.java:37) ~[classes!/:0.0.1-SNAPSHOT]
        at org.littlewings.spring.batch.BookTasklet$$FastClassBySpringCGLIB$$1150977e.invoke(<generated>) ~[classes!/:0.0.1-SNAPSHOT]

〜省略〜

Taskletの場合はトランザクションはひとつなので、すべてのデータがロールバックしましたね。

mysql> select * from book;
Empty set (0.00 sec)

Exceptionをスローしてみます。

        for (Book book : books) {
            currentCount++;

            if (currentCount > 7) {
                // throw new RuntimeException("Oops!!");
                throw new Exception("Oops!!");
            }

            entityManager.persist(book);
            entityManager.flush();
        }

ログ。

2022-05-27 01:41:41.628  INFO 23266 --- [           main] o.s.b.a.b.JobLauncherApplicationRunner   : Running default command line with: []
2022-05-27 01:41:41.962  INFO 23266 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=taskletJob]] launched with the following parameters: [{run.id=3}]
2022-05-27 01:41:42.092  INFO 23266 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Executing step: [taskletStep]
2022-05-27 01:41:42.093 DEBUG 23266 --- [           main] o.s.batch.core.step.AbstractStep         : Executing: id=10
2022-05-27 01:41:42.254 DEBUG 23266 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Applying contribution: [StepContribution: read=0, written=0, filtered=0, readSkips=0, writeSkips=0, processSkips=0, exitStatus=EXECUTING]
2022-05-27 01:41:42.255 DEBUG 23266 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Rollback for Exception: java.lang.Exception: Oops!!
2022-05-27 01:41:42.260 DEBUG 23266 --- [           main] o.s.t.support.TransactionTemplate        : Initiating transaction rollback on application exception

org.springframework.batch.core.step.tasklet.UncheckedTransactionException: java.lang.Exception: Oops!!
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:487) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:331) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140) ~[spring-tx-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.step.tasklet.TaskletStep$2.doInChunkContext(TaskletStep.java:273) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:82) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:375) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:145) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:258) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:208) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:152) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.AbstractJob.handleStep(AbstractJob.java:413) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.SimpleJob.doExecute(SimpleJob.java:136) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:320) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:149) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50) ~[spring-core-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:140) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
        at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:128) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at jdk.proxy2/jdk.proxy2.$Proxy83.run(Unknown Source) ~[na:na]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.execute(JobLauncherApplicationRunner.java:199) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.executeLocalJobs(JobLauncherApplicationRunner.java:173) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.launchJobFromProperties(JobLauncherApplicationRunner.java:160) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:155) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:150) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:762) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:752) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.littlewings.spring.batch.App.main(App.java:11) ~[classes!/:0.0.1-SNAPSHOT]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
        at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:108) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:58) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:65) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
Caused by: java.lang.Exception: Oops!!
        at org.littlewings.spring.batch.BookTasklet.execute(BookTasklet.java:38) ~[classes!/:0.0.1-SNAPSHOT]
        at org.littlewings.spring.batch.BookTasklet$$FastClassBySpringCGLIB$$1150977e.invoke(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:137) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:124) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.littlewings.spring.batch.BookTasklet$$EnhancerBySpringCGLIB$$54aa2694.execute(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:407) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        ... 46 common frames omitted

2022-05-27 01:41:42.288 ERROR 23266 --- [           main] o.s.batch.core.step.AbstractStep         : Encountered an error executing step taskletStep in job taskletJob

java.lang.Exception: Oops!!
        at org.littlewings.spring.batch.BookTasklet.execute(BookTasklet.java:38) ~[classes!/:0.0.1-SNAPSHOT]
        at org.littlewings.spring.batch.BookTasklet$$FastClassBySpringCGLIB$$1150977e.invoke(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:137) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:124) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.littlewings.spring.batch.BookTasklet$$EnhancerBySpringCGLIB$$54aa2694.execute(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:407) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:331) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140) ~[spring-tx-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.step.tasklet.TaskletStep$2.doInChunkContext(TaskletStep.java:273) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:82) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:375) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:145) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:258) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:208) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:152) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.AbstractJob.handleStep(AbstractJob.java:413) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.SimpleJob.doExecute(SimpleJob.java:136) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:320) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:149) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50) ~[spring-core-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:140) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
        at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:128) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215) ~[spring-aop-5.3.20.jar!/:5.3.20]
        at jdk.proxy2/jdk.proxy2.$Proxy83.run(Unknown Source) ~[na:na]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.execute(JobLauncherApplicationRunner.java:199) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.executeLocalJobs(JobLauncherApplicationRunner.java:173) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.launchJobFromProperties(JobLauncherApplicationRunner.java:160) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:155) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:150) ~[spring-boot-autoconfigure-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:762) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:752) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295) ~[spring-boot-2.7.0.jar!/:2.7.0]
        at org.littlewings.spring.batch.App.main(App.java:11) ~[classes!/:0.0.1-SNAPSHOT]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
        at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:108) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:58) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:65) ~[batch-transaction-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]

2022-05-27 01:41:42.297  INFO 23266 --- [           main] o.s.batch.core.step.AbstractStep         : Step: [taskletStep] executed in 203ms
2022-05-27 01:41:42.362 DEBUG 23266 --- [           main] o.s.batch.core.step.AbstractStep         : Step execution complete: StepExecution: id=10, version=2, name=taskletStep, status=FAILED, exitStatus=FAILED, readCount=0, filterCount=0, writeCount=0 readSkipCount=0, writeSkipCount=0, processSkipCount=0, commitCount=0, rollbackCount=1
2022-05-27 01:41:42.411  INFO 23266 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=taskletJob]] completed with the following parameters: [{run.id=3}] and the following status: [FAILED] in 377ms

ロールバックしました。

2022-05-27 01:41:42.260 DEBUG 23266 --- [           main] o.s.t.support.TransactionTemplate        : Initiating transaction rollback on application exception

org.springframework.batch.core.step.tasklet.UncheckedTransactionException: java.lang.Exception: Oops!!
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:487) ~[spring-batch-core-4.3.6.jar!/:4.3.6]

〜省略〜

チャンクの時と同じように、UncheckedTransactionExceptionにラップされてスローされていますね。ロールバックの理由については
Exceptionとして扱っていますが。

データもロールバックされています。

mysql> select * from book;
Empty set (0.00 sec)

RuntimeExceptionExceptionスタックトレースをちょっと比較。

RuntimeException

2022-05-27 01:39:53.320 DEBUG 23081 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Rollback for RuntimeException: java.lang.RuntimeException: Oops!!
2022-05-27 01:39:53.326 DEBUG 23081 --- [           main] o.s.t.support.TransactionTemplate        : Initiating transaction rollback on application exception

java.lang.RuntimeException: Oops!!
        at org.littlewings.spring.batch.BookTasklet.execute(BookTasklet.java:37) ~[classes!/:0.0.1-SNAPSHOT]
        at org.littlewings.spring.batch.BookTasklet$$FastClassBySpringCGLIB$$1150977e.invoke(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.20.jar!/:5.3.20]

〜省略〜

Exception

2022-05-27 01:41:42.255 DEBUG 23266 --- [           main] o.s.batch.core.step.tasklet.TaskletStep  : Rollback for Exception: java.lang.Exception: Oops!!
2022-05-27 01:41:42.260 DEBUG 23266 --- [           main] o.s.t.support.TransactionTemplate        : Initiating transaction rollback on application exception

org.springframework.batch.core.step.tasklet.UncheckedTransactionException: java.lang.Exception: Oops!!
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:487) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:331) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140) ~[spring-tx-5.3.20.jar!/:5.3.20]
        at org.springframework.batch.core.step.tasklet.TaskletStep$2.doInChunkContext(TaskletStep.java:273) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:82) ~[spring-batch-core-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:375) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:145) ~[spring-batch-infrastructure-4.3.6.jar!/:4.3.6]
        at org.springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:258) ~[spring-batch-core-4.3.6.jar!/:4.3.6]

〜省略〜

Caused by: java.lang.Exception: Oops!!
        at org.littlewings.spring.batch.BookTasklet.execute(BookTasklet.java:38) ~[classes!/:0.0.1-SNAPSHOT]
        at org.littlewings.spring.batch.BookTasklet$$FastClassBySpringCGLIB$$1150977e.invoke(<generated>) ~[classes!/:0.0.1-SNAPSHOT]
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.20.jar!/:5.3.20]

〜省略〜

これで、Taskletの場合もスローされる例外がRuntimeExceptionであってもExceptionであってもロールバックされることがわかりましたね。

ソースコードは、元に戻しておきます。

        for (Book book : books) {
            currentCount++;

            if (currentCount > 7) {
                // throw new RuntimeException("Oops!!");
                // throw new Exception("Oops!!");
            }

            entityManager.persist(book);
            entityManager.flush();
        }

少し追ってみる

ここまで見てきて、各要素からExceptionがスローされた場合は、RepeatExceptionまたはUncheckedTransactionExceptionにラップされて
再度スローされることがわかりました。

また、この時のItemProcessorItemWriterTaskletスタックトレースはすべて同じであり、ラップされる例外も
UncheckedTransactionExceptionです。ItemReaderだけ異なり、ラップされる例外はRepeatExceptionですね。

ここで少し、Spring Batchの実装を見てみましょう。

ItemReaderで例外を扱っていた(再スローしていた)のは、こちらでした。

https://github.com/spring-projects/spring-batch/blob/4.3.6/spring-batch-infrastructure/src/main/java/org/springframework/batch/repeat/support/RepeatTemplate.java#L312-L322

ErrorRuntimeExceptionExceptionそれぞれを見ています。Exceptionの場合は、RepeatExceptionという例外でラップして
再スローですね。

ItemProcessorItemWriterTaskletの場合はこちら。

https://github.com/spring-projects/spring-batch/blob/4.3.6/spring-batch-core/src/main/java/org/springframework/batch/core/step/tasklet/TaskletStep.java#L467-L488

ErrorRuntimeExceptionExceptionそれぞれを見ています。Exceptionの場合は、UncheckedTransactionExceptionという
例外にラップされてスローされます。

https://github.com/spring-projects/spring-batch/blob/4.3.6/spring-batch-core/src/main/java/org/springframework/batch/core/step/tasklet/TaskletStep.java#L487

https://github.com/spring-projects/spring-batch/blob/4.3.6/spring-batch-core/src/main/java/org/springframework/batch/core/step/tasklet/TaskletStep.java#L277

そして、いずれの構成要素であっても、RuntimeExceptionException問わず、例外がスローされたらロールバックされることが
確認できました、と。

まとめ

Spring Batchのトランザクション管理で、スローされる例外がRuntimeExceptionExceptionに関わらずロールバックされることを
確認してみました。

なんとなくExceptionでもロールバックするのだろうと思っていましたが、実際に確認しておくと安心かなと。

Exceptionの場合は、内部的には別の例外でラップされて再スローされていることもわかりましたし。