Spring

【SpringBatch】メタデータテーブルをDBに生成せずに済ませるには(検証ソースあり)

2022年12月2日

Spring Batchは、Spring Frameworkファミリーの中でも、バッチ処理に特化したフレームワークです。

spring.pleiades.io

非常に便利な機能が揃っていますが、重厚ゆえ理解が若干難しいのと、高機能すぎる部分があります。※個人の感想です

この記事では高機能すぎるデフォルト機能である「メタデータテーブル」をDBに作らずにSpring Batchを起動する方法をお伝えします。

ソースコード例と、各種ライブラリのVerも指定して書いてあるので、ぜひご一読ください!

ITエンジニア6年目の山根です。X(Twitter)やってます。自己紹介,お問い合わせはこちらまで!

この記事で伝えたいこと

  • メタデータテーブルをDBに生成せずSpring Batchを起動する方法

解決したい課題

  • バッチ処理のFWとしてSpring Batchを使う必要がある。
  • 諸事情あってメタデータテーブルを作るのが面倒なんだけど、作らないとバッチ処理が落ちることに気づいた。
  • なんとかメタデータテーブル作らずにバッチ起動できる方法はないものか?

といったあなた(開発者)の課題を解決します。(結構ニッチなニーズかもしれません)

メタデータテーブルとは

Spring Batchは起動後、メタデータテーブルと呼ばれるテーブル群に情報を出し入れしながら処理を進めていきます。

spring.pleiades.io

このテーブル群は、そのバッチが解決したいドメインの情報を管理するものではなく、Spring Batch固有の処理単位("Job"や"Step")の進行状況を管理するものです。

SpringBatchを起動したい場合は、みなさんが永続化のため用いているオンディスクDB(MySQLとか,PostgreSQLとか...)に対してCREATE文を流し、これらのテーブルを作成する必要があります。後述しますが、流さないとバッチ処理自体が落ちます

失敗時のリトライ等に使えそうだし、ジョブ情報が残ってくれるのは便利っちゃ便利なのですが、以下のような場合は「CREATE文流してまでテーブルに実行情報残してもらわなくても良くない?」というケースが想定されます。

他のジョブ管理ツールで代替できるやん

Azure BatchAWS Batch等、クラウドサービスを使えばUI上でジョブ情報が閲覧できます。現場によっては「お金はかかるものの、これ使った方が見やすくて運用しやすいしイイジャン」という判断もあり得ます。

テーブル生成が面倒なんだが

ちょっと怠惰な判断かもしれませんが、組織によってはメタデータテーブルを作る調整を行う時間を省きたい場合があるかなと思ってます。
バッチ処理をパッパと作ってしまってさっさとビジネスに組み込みたい、みたいな。

また、以下のQiita記事の方は「組織のRDBの権限設定によりそもそもテーブル生成ができない」そうです。確かに、そんな状況もあり得そうです。

qiita.com

解決方法

以下本題。「メタデータテーブルを使わずSpring Batchを起動する方法」

概要(インメモリDBに流す)

決論から言うと、「メタデータテーブル用のDBを追加で作り、メタデータはそこに流す。その際DBはインメモリDBで生成し、バッチ終了と共に揮発するようにする」という手法を取ります。

実演

以下、実演です。言語はKotlin。

ライブラリVer用途
JDKopenjdk-17
Spring Batch4.3.3バッチ実行ライブラリ
SQL Server2019-latest永続化用(ビジネスデータ)
H21.4.197永続化用(メタデータ)

動かない(ダメなパターン)

github.com

README.mdの起動方式に沿ってバッチを実行すると、以下のようなエラーが出ます。'BATCH_JOB_INSTANCE'はメタデータテーブルの一種です。

Caused by: org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; bad SQL grammar [SELECT JOB_INSTANCE_ID, JOB_NAME from BATCH_JOB_INSTANCE where JOB_NAME = ? and JOB_KEY = ?]; nested exception is com.microsoft.sqlserver.jdbc.SQLServerException: Invalid object name 'BATCH_JOB_INSTANCE'.

前述した、「Spring Batchはデフォルトだとメタデータテーブルに値を出し入れしようとする。メタデータテーブルが作られてない場合は処理が落ちる」はこのエラーが示してくれています。

Datasourceを分ける(ちゃんと実行できるパターン)

github.com

対応としては以下のような形になります。

  • DataSource,JdbcTemplateを返すBeanをそれぞれ1つずつ作る
    • 1つはBusinessData用
    • 1つはMetaData用
@Configuration
class DataSourceConfiguration {

    @Bean
    @Primary
    fun businessDataSource(): DataSource{
        return DataSourceBuilder.create()
        //ほんとはapplication.ymlを経て環境変数から取得するのがベター
            .driverClassName("{ビジネスデータを貯めるDBに接続する用のDriver名}")
            .url("{接続文字列}")
            .username("{接続するユーザ}")
            .password("{接続するパスワード}")
            .build()
    }

    @Bean
    fun businessDataJdbcTemplate(@Qualifier("businessDataSource") dataSource: DataSource): JdbcTemplate {
            return JdbcTemplate(dataSource)
    }

    @Bean
    @BatchDataSource
    fun metaDataSource(): DataSource {
            return EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.H2)
            .addScripts("/org/springframework/batch/core/schema-h2.sql") //H2DBが持ってるメタデータテーブル生成スクリプト
            .build()
    }

    @Bean
    fun metaDataJdbcTemplate(@Qualifier("metaDataSource") dataSource: DataSource): JdbcTemplate {
        return JdbcTemplate(dataSource)
    }
}

また、同じ型のBeanが二つできることになるので、呼び出す側でも"どっち呼ぶ?”を指定してあげる必要があります。@Qualifierを使いましょう。
※この辺はSpringの基礎話なので省略

@Qualifier("businessDataJdbcTemplate")
@Autowired lateinit var jdbcTemplate: JdbcTemplate
   

この対応を入れると、ちゃんと起動してくれます。

留意点

推奨される方法かどうか不明

ここまで偉そうに述べておいて恐縮ですが、山根が調べた限り、Spring Batch公式手順として載っているものではなく、推奨されるものかどうか不明です。

一旦の"回避策"として捉えて、みなさんのプロジェクト内で議論の上利用するようにしてくださいm( )m

最新のH2Databaseだと使えない

検証中に気づいたのですが、H2DBのVerを1.4.197まで落とさないと使えないっぽいです。

https://mvnrepository.com/artifact/com.h2database/h2

2022/12/2時点での最新Ver2.1.124Verおよび、ユーザ数最大の1.4.200Verについては以下のようなエラーが出ます。

Caused by: java.lang.ClassNotFoundException: org.h2.Driver
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) ~[na:na]
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) ~[na:na]

同じエラー食らっている方も見つけました。

github.com

もしかしたらJdbcTemplateとかSpring BatchのVerと互換性がないのかもしれませんね。
最新でないH2には脆弱性も報告されているっぽいですし、最新H2使って本記事の手法が実現できるかどうかは、追って調べてみます。

サーバを常時起動させる時は注意

設計上、サーバが常時起動するようになる場合は注意が必要です。(Batch処理の話をしているのでほとんどの場合大丈夫だと思いますが)

インメモリDBにずっとデータが溜まっていくので、メモリ領域が圧迫され、性能劣化が予想されます。

-Spring