はじめに (対象読者・この記事でわかること)

この記事は、JavaEE(Jakarta EE)での開発経験があり、エンタープライズ向けの大規模バッチ処理を検討しているエンジニアを対象としています。特に、既存のJavaEEプロジェクトにSpring Batchを導入したい、あるいはSpring Boot ではなく従来のサーブレットコンテナ上でバッチジョブを動かしたいと考えている方に最適です。本稿を読むことで、次のことが理解・実装できるようになります。

  • JavaEE と Spring Batch の相性や注意点
  • Maven(または Gradle)での依存関係設定方法
  • javax.batch ではなく Spring Batch を利用したジョブ定義とリスナーの作成手順
  • コンテナ(例:WildFly、Tomcat)上でジョブを起動・管理する具体的なコード例

背景として、近年 JavaEE のサポートが Jakarta EE に移行したことで、従来のバッチ API(JSR 352)だけでなく、Spring が提供する高度な機能を活用したいニーズが高まっています。そのため、本記事では「Spring Batch を JavaEE アプリケーションに組み込む」実務的なハンドブックを提供します。

前提知識

この記事を読み進める上で、以下の知識があるとスムーズです。

  • Java の基本文法とオブジェクト指向概念
  • JavaEE(Jakarta EE)アプリケーションの構造とデプロイ方法(WAR/EAR)
  • Maven(または Gradle)を用いたビルド管理の基本操作
  • Spring Framework の基礎概念(DI、コンテナ、Bean 定義)

Spring Batch の概要と JavaEE での導入背景

Spring Batch は、トランザクション管理、ジョブの再実行、スキップ機能、パーティショニングなど、エンタープライズ向けバッチ処理に必要な機能を網羅したフレームワークです。もともとは Spring Boot と組み合わせたシンプルな構成が主流でしたが、実は Spring の DI コンテナだけを利用すれば、JavaEE のサーブレットコンテナ上でも問題なく動作 します。

なぜ JavaEE と組み合わせるか

  1. 既存資産の活用
    大規模なレガシーシステムでは、すでに JavaEE(Jakarta EE)のアプリケーションサーバー(WildFly、WebSphere、Payara など)上に多数の EJB、JPA、JMS が稼働しています。これらを全面的に Spring Boot へ置き換えるコストは膨大です。

  2. 運用・保守の一元化
    バッチ処理だけを Spring Boot に切り出すと、サーバー管理が二元化します。JavaEE アプリケーションサーバー上で Spring Batch を動かすことで、デプロイ・モニタリングを同一プラットフォームで一元化できます。

  3. 高度な機能の取得
    JSR 352 の標準バッチ API はシンプルですが、スキップやリトライ、パーティショニングといった高度機能は Spring Batch が圧倒的に優れています。

基本構成

コンポーネント 目的
JobLauncher バッチジョブ起動インターフェイス
Job バッチ処理の全体フロー(ステップ集合)
Step 1 つの処理単位(Reader → Processor → Writer)
ItemReader/ItemWriter/ItemProcessor 入出力・変換ロジック
JobRepository ジョブ実行履歴の永続化(DB)
TransactionManager トランザクション管理(JTA と統合可)

以上の構成を、JavaEE の web.xmljboss-deployment-structure.xml で定義した Spring コンテキストに組み込むだけで、バッチジョブは通常のサーブレットリクエストと同様に起動できます。

Spring Batch を JavaEE アプリケーションに組み込む手順

以下では、WildFly 27 を例に、WAR ファイルに Spring Batch を組み込み、HTTP エンドポイントからジョブを起動するまでの流れをステップごとに示します。

ステップ 1 : プロジェクト構成と依存関係の設定

pom.xml に必要な Spring Batch と Spring Context の依存を追加します。JavaEE の API は provided スコープに指定し、コンテナが提供することを明示します。

Xml
<project xmlns="http://maven.apache.org/POM/4.0.0" ...> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>javaee-springbatch-demo</artifactId> <version>1.0.0</version> <packaging>war</packaging> <properties> <java.version>11</java.version> <spring.version>6.1.4</spring.version> <spring.batch.version>5.1.1</spring.batch.version> </properties> <dependencies> <!-- Spring Core --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <!-- Spring Batch Core --> <dependency> <groupId>org.springframework.batch</groupId> <artifactId>spring-batch-core</artifactId> <version>${spring.batch.version}</version> </dependency> <!-- Spring Batch Infrastructure (JPA, JDBC) --> <dependency> <groupId>org.springframework.batch</groupId> <artifactId>spring-batch-infrastructure</artifactId> <version>${spring.batch.version}</version> </dependency> <!-- JPA (Hibernate) --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>6.4.0.Final</version> </dependency> <!-- JavaEE API (provided) --> <dependency> <groupId>jakarta.platform</groupId> <artifactId>jakarta.jakartaee-api</artifactId> <version>10.0.0</version> <scope>provided</scope> </dependency> <!-- Logging --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>2.0.9</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.4.12</version> </dependency> </dependencies> <build> <finalName>javaee-springbatch-demo</finalName> </build> </project>

ポイント

  • spring-batch-corespring-batch-infrastructure を必ず入れる。
  • javax.batch(JSR 352)系は使用しないので除外。
  • jakarta.jakartaee-apiprovided にして、WildFly が同梱する API と衝突しないようにします。

ステップ 2 : Spring コンテキストと Batch 設定ファイルの作成

src/main/webapp/WEB-INF/spring-batch-config.xml に Batch の Bean 定義を書きます。

Xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:batch="http://www.springframework.org/schema/batch" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/batch https://www.springframework.org/schema/batch/spring-batch.xsd"> <!-- データソースは JavaEE が提供する JTA データソースを参照 --> <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close"> <property name="url" value="jdbc:h2:mem:test;DB_CLOSE_DELAY=-1"/> <property name="driverClassName" value="org.h2.Driver"/> <property name="username" value="sa"/> <property name="password" value=""/> </bean> <!-- JTA Transaction Manager(WildFly が提供) --> <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/> <!-- JobRepository(バッチの履歴情報) --> <bean id="jobRepository" class="org.springframework.batch.core.repository.support.JobRepositoryFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="transactionManager" ref="transactionManager"/> <property name="isolationLevelForCreate" value="ISOLATION_DEFAULT"/> </bean> <!-- JobLauncher --> <bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher"> <property name="jobRepository" ref="jobRepository"/> </bean> <!-- Reader / Processor / Writer の実装は別クラスで管理 --> <bean id="sampleItemReader" class="com.example.batch.SampleItemReader"/> <bean id="sampleItemProcessor" class="com.example.batch.SampleItemProcessor"/> <bean id="sampleItemWriter" class="com.example.batch.SampleItemWriter"/> <!-- Step 定義 --> <batch:step id="sampleStep"> <batch:tasklet> <batch:chunk reader="sampleItemReader" processor="sampleItemProcessor" writer="sampleItemWriter" commit-interval="100"/> </batch:tasklet> </batch:step> <!-- Job 定義 --> <batch:job id="sampleJob" xmlns="http://www.springframework.org/schema/batch"> <batch:step id="step1" ref="sampleStep"/> </batch:job> </beans>

重要ポイント

  • データソース は例としてインメモリ H2 を使用していますが、実際には WildFly の JTA データソース(java:jboss/datasources/ExampleDS 等)を JNDI ルックアップで取得すれば本番環境でも同様に使えます。
  • TransactionManagerJtaTransactionManager を指定することで、EJB や JPA と同一トランザクションコンテキストを共有できます。
  • batch:jobbatch:step は XML 記法ですが、@Configuration@EnableBatchProcessing アノテーションでも同様に記述可能です。

ステップ 3 : Web レイヤでジョブ起動エンドポイントを実装

src/main/java/com/example/web/BatchController.java を作成し、サーブレットコンテナ上の HTTP リクエストからジョブを起動します。

Java
package com.example.web; import jakarta.inject.Inject; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.batch.core.Job; import org.springframework.batch.core.JobParametersBuilder; import org.springframework.batch.core.launch.JobLauncher; import java.io.IOException; @WebServlet("/run-batch") public class BatchController extends HttpServlet { @Inject private JobLauncher jobLauncher; @Inject private Job sampleJob; // spring-batch-config.xml の <batch:job id="sampleJob"/> が自動的に注入 @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { try { jobLauncher.run(sampleJob, new JobParametersBuilder() .addLong("timestamp", System.currentTimeMillis()) .toJobParameters()); resp.getWriter().write("Batch job started successfully."); } catch (Exception e) { resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); resp.getWriter().write("Failed to start batch job: " + e.getMessage()); } } }

ポイント

  • @Inject(JSR‑330)を使うことで、JavaEE の CDI コンテナから Spring の Bean を取得できる。
  • JobLauncher.run() の第 2 引数に JobParameters を渡すことで、ジョブの再実行やパラメータ化が可能。
  • エラーは HTTP 500 として返すだけでなく、logback へも出力しておくと運用時に追跡しやすい。

ステップ 4 : デプロイと動作確認

  1. mvn clean package で WAR をビルド
  2. WildFly の管理コンソール(または CLI)で deploy javaee-springbatch-demo.war
  3. ブラウザで http://localhost:8080/javaee-springbatch-demo/run-batch にアクセス

レスポンスが Batch job started successfully. と表示されれば成功です。サーバーログに以下のようなエントリが出力されます。

2025-09-15 10:23:45.123 INFO  [com.example.batch.SampleItemReader] - Reading record #1
2025-09-15 10:23:45.128 INFO  [org.springframework.batch.core.job.SimpleJob] - Job: [sampleJob] completed with status: COMPLETED

ハマった点やエラー解決

1. Spring Bean が注入されない(null になる)

原因web.xml に Spring の ContextLoaderListener が登録されていない。
解決策WEB-INF/web.xml に以下を追記

Xml
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/spring-batch-config.xml</param-value> </context-param>

2. javax.naming.NameNotFoundException: java:comp/DefaultDataSource が出る

原因:データソースを JNDI で取得しようとしているが、WildFly にデータソースがデプロイされていない。
解決策:WildFly の管理コンソールでデータソース ExampleDS を有効化し、spring-batch-config.xmldataSource 定義を org.springframework.jndi.JndiObjectFactoryBean に変更。

Xml
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName" value="java:jboss/datasources/ExampleDS"/> </bean>

3. Job が即座に FAILED になる

原因JobRepository のテーブルが未作成。
解決策:Spring Batch が提供する org/springframework/batch/core/schema-h2.sql(または schema-mysql.sql)を起動時に自動実行させるか、WildFly のデータソースに対して手動で DDL を適用。

Sql
-- 例: H2 用 DDL CREATE TABLE BATCH_JOB_INSTANCE (JOB_INSTANCE_ID BIGINT NOT NULL PRIMARY KEY, VERSION BIGINT, JOB_NAME VARCHAR(100) NOT NULL, JOB_KEY VARCHAR(32) NOT NULL, CONSTRAINT JOB_INST_UN UNIQUE (JOB_NAME, JOB_KEY)); -- (以下省略)

完全な構成図(テキスト版)

[HTTP リクエスト] --> [BatchController(Servlet)] --> [JobLauncher] --> [Job (sampleJob)]
        |                                             |
        |                                             v
        |                                   [Step (sampleStep)]
        |                                             |
        |                  +--------------------------+--------------------------+
        |                  |                         |                          |
        v                  v                         v                          v
    [Reader]          [Processor]               [Writer]                [TransactionManager]
        |                  |                         |                          |
        +------------------+-------------------------+--------------------------+
                                      |
                               [DataSource (JTA) / DB]

上記構成により、JavaEE の標準的なデプロイメントユニット(WAR)だけで、Spring Batch のフル機能が利用可能です。

まとめ

本記事では、JavaEE(Jakarta EE)環境に Spring Batch を組み込む方法 を、実際のプロジェクト構成・設定ファイル・サンプルコードを交えて解説しました。

  • JavaEE と Spring Batch の相性:既存のサーブレットコンテナ上でも問題なく動作し、JTA とシームレスに統合できる。
  • 導入手順:Maven で依存追加 → Spring Batch 用 XML コンテキスト作成 → Servlet で Job 起動エンドポイント実装 → デプロイ・動作確認。
  • トラブルシューティング:Bean 注入失敗、JNDI データソース未設定、JobRepository のテーブル未作成といった典型的なエラーとその解決策を提示。

この手順を踏むことで、レガシー JavaEE アプリケーションに高度なバッチ処理機能をスムーズに追加 でき、システム全体の保守性と拡張性が向上します。今後は、ジョブのスケジューリング(Quartz 連携)や、マルチノードでのパーティショニング、Spring Cloud Data Flow への展開といったテーマにも挑戦していく予定です。

参考資料