83.参考: SpringBatchテーブルのクリーンアップ

概要

Spring Batchではバッチ実行結果をDB(SpringBatch Metadata tables)に保存します。

実行するたびにジョブやステップの実行結果が登録されるため、テーブルの古いレコードをクリーニングしたくなります。

テーブルスキーマが分かっていれば誰でもクリーンアップツールは作成できますが、

参考までに記事として触れてみようと思います。

はじめに

ここでは、SpringBatchの実行結果を保存するテーブルのうち、指定した月数以上過ぎているレコードを対象に

データを削除するバッチを作ります。

バッチはSpringBatchで作ります。

注意点はdelete tableを実行する順番です。テーブルにFK(外部キー)が使用されているからです。

すでに存在するツールキット(Tasklet)の紹介

実はすでにそのようなツール(Tasklet)を作っている人はいますので、まずはそのWEBサイトを紹介します。

SpringBatchのTaskletになっていて、SpringBatchのコンフィグにTaskletとして設定すれば使用できるようになっています。

https://github.com/arey/spring-batch-toolkit

(Spring BatchメタデータテーブルをクリーンアップするTasklet)

SpringBatchの設定ファイルのみによるクリーンアップバッチの作成

上記のツールを使用すれば簡単にテーブルのクリーンアップツールは作成できます。

ここでは、SpringBatchのXML設定ファイルのみによるクリーンアップのバッチを作成することに挑戦してみます。

XML設定ファイルのみによるバッチ作成の利点は、Javaのコンパイルが不要なことです。

また、コンパイルなしにXMLの記述変更だけで動作を変えられます。

Taskletでも、XMLファイルの方法でも、どちらを採用するかは趣味の問題かとは思いますが、選択肢があって、いろいろな方法を知っておくことは良いことだと思います。

SpringBatch設定ファイル作成で使用した技術

参考までにここで使用した技術を記述します。興味あれば1つ1つ見てみてください。

●外部のプロパティファイルの読み込みとデフォルト値の設定

context:property-placeholderタグで外部プロパティファイルを読み込めます。

また、プロパティファイルに項目が存在しないときに設定されるデフォルト値もproperties-ref属性で設定できます。

今回使用していませんが、システムプロパティや環境変数を使用するためのモード設定をする属性もあります。

●Spring EL式

startDate(削除開始日付)を計算するのに使用しています。

●オンメモリのJobRepository

このバッチはSpringBatchでできていますので、そのままではクリーンアップのバッチが起動するたびに

DBに実行結果のレコードができてしまいます。

クリーンアップするバッチ自身がレコードを作ってしまうのも微妙なので、

オンメモリのJobRepositoryに保存するようにしました。

splitなどの一部のマルチスレッドな処理に適合していないようです(SpringBatch3.0.7現在)。

このバッチではマルチスレッド処理を使用していないので使用して大丈夫です。

オンメモリのJobRepositoryとその注意点は「05.使用するための準備」で触れています。

●ListItemReader

通常はItemReaderはデータ(item)を固定で指定できませんが、

ListItemReaderはチャンク処理で読み込むデータ(item)を設定XMLファイル上に直接記述できます。

itemはMapでkey="date" value=startDate(削除開始日付)を設定し、1回だけループするように設定しました。

●abstractでbeanタグのオーバーライド

ItemWriterで同じようなbeanを複数記述するので、propertyタグを省略できるようにabstractを使用しました。

参考:2.ApplicationContext.xmlファイルの記述方法

●CompositeItemWriter

通常1つのItemWriterしか設定できませんが、複数のSQLのDelete文を実行したかったのでCompositeItemWriterを

使用しました。上記のabstractと組み合わせて使用しています。

SpringBatchのバージョンの違いよる影響

Spring Batchは2.x系と3.x系のバージョンでテーブルスキーマが違うことに注意してください。

ここでは3.x系を扱ってみようと思います。

とはいっても2.x系に対応するのは簡単で、削除するテーブル名をJOB_EXECUTION_PARAMS ⇒ BATCH_JOB_PARAMSに書き変えるだけです。

必要なJarファイル

SpringBatchテーブルのクリーンアップに必要なJarを以下に示します。

POM形式で書いていますが、POMを使用したことがない人でも見れば必要なものは分かるかと思います。

<properties>

<spring.version>4.2.4</spring.version>

<main.class>org.springframework.batch.core.launch.support.CommandLineJobRunner</main.class>

<main.batch.name>test</main.batch.name>

</properties>

<dependencies>

<dependency>

<groupId>commons-dbcp</groupId>

<artifactId>commons-dbcp</artifactId>

<version>1.4</version>

</dependency>

<dependency>

<groupId>joda-time</groupId>

<artifactId>joda-time</artifactId>

<version>2.9.4</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-core</artifactId>

<version>${spring.version}.RELEASE</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-beans</artifactId>

<version>${spring.version}.RELEASE</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-jdbc</artifactId>

<version>${spring.version}.RELEASE</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-test</artifactId>

<version>${spring.version}.RELEASE</version>

</dependency>

<dependency>

<groupId>org.springframework.batch</groupId>

<artifactId>spring-batch-core</artifactId>

<version>3.0.7.RELEASE</version>

</dependency>

</dependencies>

SpringBatchテーブルのクリーンアップバッチのソース

バッチXMLファイル(cleaner-context.xml)

以下がバッチを定義したXMLファイルの中身です。

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:p="http://www.springframework.org/schema/p"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:util="http://www.springframework.org/schema/util"

xmlns:context="http://www.springframework.org/schema/context"

xsi:schemaLocation="http://www.springframework.org/schema/batch

http://www.springframework.org/schema/batch/spring-batch-3.0.xsd

http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

http://www.springframework.org/schema/util

http://www.springframework.org/schema/util/spring-util.xsd

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context-4.2.xsd">

<!--

=================================================

Spring Batch Meata Data Table Clear バッチ(Spring Batch 3.x用)

補足:もしSpring Batch 2.xに使用したい場合は以下の部分を書き換えてください。

ItemWriterのbeanタグ内の JOB_EXECUTION_PARAMS ⇒ BATCH_JOB_PARAMSに書き変える。

=================================================

-->

<!-- プロパティファイルの設定

このファイルと同じパスにcleaner.propertiesファイルを作ると設定を読み込む。設定項目は下のdefaultPropertiesを参照。

もし、プロパティファイルを使いたくない場合は、以下のdefaultPropertiesを直接書き変えてもよい。

-->

<context:property-placeholder

ignore-resource-not-found="true"

location="classpath:cleaner.properties"

properties-ref="defaultProperties"

/>

<!-- プロパティのデフォルト値 -->

<util:properties id="defaultProperties">

<prop key="props.daymonths">12</prop>

<prop key="props.db.PREFIX">BATCH_</prop>

<prop key="props.db.driverClassName">org.postgresql.Driver</prop>

<prop key="props.db.url"></prop>

<prop key="props.db.username"></prop>

<prop key="props.db.password"></prop>

</util:properties>

<!-- 残しておく日数 -->

<bean id="startDate" class="java.util.Date" >

<constructor-arg index="0" value="#{

new org.joda.time.DateTime().minusMonths(${props.months}).toDate().getTime()

}" />

</bean>

<!-- SpringBatchの設定(バッチの実行情報はオンメモリのリポジトリに保存。DBには保存されない。) -->

<bean id="jobOperator" class="org.springframework.batch.core.launch.support.SimpleJobOperator"

p:jobLauncher-ref="jobLauncher" p:jobExplorer-ref="jobExplorer"

p:jobRepository-ref="jobRepository" p:jobRegistry-ref="jobRegistry" />

<bean id="jobExplorer" class="org.springframework.batch.core.explore.support.JobExplorerFactoryBean"

p:dataSource-ref="jobDataSource" />

<bean id="jobRegistry" class="org.springframework.batch.core.configuration.support.MapJobRegistry" />

<bean class="org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor">

<property name="jobRegistry" ref="jobRegistry"/>

</bean>

<bean id="jobLauncher"

class="org.springframework.batch.core.launch.support.SimpleJobLauncher">

<property name="jobRepository" ref="jobRepository" />

</bean>

<bean id="jobRepository"

class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean"

p:transactionManager-ref="jobTransactionManager" />

<!-- ダミーのDataSource。 -->

<bean id="jobDataSource" class="org.springframework.jdbc.datasource.SingleConnectionDataSource" />

<bean id="jobTransactionManager" class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />

<!-- Data Source (削除対象のSpringBatchのMetaデータテーブルの接続先)-->

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">

<property name="driverClassName" value="${props.db.driverClassName}"/>

<property name="url" value="${props.db.url}" />

<property name="username" value="${props.db.username}" />

<property name="password" value="${props.db.password}" />

</bean>

<bean id="transactionManager"

class="org.springframework.jdbc.datasource.DataSourceTransactionManager" lazy-init="true">

<property name="dataSource" ref="dataSource" />

</bean>

<!-- ジョブの設定 -->

<job id="job1" xmlns="http://www.springframework.org/schema/batch"

incrementer="jobParametersIncrementer">

<step id="step1" parent="simpleStep">

<tasklet>

<chunk reader="itemReader" writer="itemWriter" />

</tasklet>

</step>

</job>

<!-- enables the functionality of JobOperator.startNextInstance(jobName) -->

<bean id="jobParametersIncrementer"

class="org.springframework.batch.core.launch.support.RunIdIncrementer" />

<bean id="simpleStep"

class="org.springframework.batch.core.step.item.SimpleStepFactoryBean" abstract="true">

<property name="jobRepository" ref="jobRepository" />

<property name="commitInterval" value="1000" />

</bean>

<!-- ItemReaderの設定 -->

<bean id="itemReader" class="org.springframework.batch.item.support.ListItemReader" >

<constructor-arg>

<util:list id="nameList" >

<util:map>

<entry key="date" value-ref="startDate" />

</util:map>

</util:list>

</constructor-arg>

</bean>

<!-- DBのItemWriterの共通設定(abstract) -->

<bean id="itemWriterDef" abstract="true" class="org.springframework.batch.item.database.JdbcBatchItemWriter" >

<property name="dataSource" ref="dataSource" />

<property name="assertUpdates" value="false" />

<property name="itemSqlParameterSourceProvider">

<bean class="org.springframework.batch.item.database.BeanPropertyItemSqlParameterSourceProvider" />

</property>

<property name="itemPreparedStatementSetter" >

<bean class="org.springframework.batch.item.database.support.ColumnMapItemPreparedStatementSetter" />

</property>

</bean>

<!-- 実際のItemWriter (バッチテーブルレコード削除)

参考:https://github.com/arey/spring-batch-toolkit

-->

<bean id="itemWriter" class="org.springframework.batch.item.support.CompositeItemWriter">

<property name="delegates">

<list>

<bean parent="itemWriterDef">

<property name="sql" ><value><![CDATA[

DELETE FROM ${props.db.PREFIX}STEP_EXECUTION_CONTEXT WHERE STEP_EXECUTION_ID

IN (SELECT STEP_EXECUTION_ID FROM ${props.db.PREFIX}STEP_EXECUTION WHERE JOB_EXECUTION_ID

IN (SELECT JOB_EXECUTION_ID FROM ${props.db.PREFIX}JOB_EXECUTION where CREATE_TIME < :date))

]]></value></property>

</bean>

<bean parent="itemWriterDef">

<property name="sql"><value><![CDATA[

DELETE FROM ${props.db.PREFIX}STEP_EXECUTION WHERE JOB_EXECUTION_ID

IN (SELECT JOB_EXECUTION_ID FROM ${props.db.PREFIX}JOB_EXECUTION where CREATE_TIME < :date)

]]></value></property>

</bean>

<bean parent="itemWriterDef">

<property name="sql"><value><![CDATA[

DELETE FROM ${props.db.PREFIX}JOB_EXECUTION_CONTEXT WHERE JOB_EXECUTION_ID

IN (SELECT JOB_EXECUTION_ID FROM ${props.db.PREFIX}JOB_EXECUTION where CREATE_TIME < :date)

]]></value></property>

</bean>

<bean parent="itemWriterDef">

<property name="sql"><value><![CDATA[

DELETE FROM ${props.db.PREFIX}JOB_EXECUTION_PARAMS WHERE JOB_EXECUTION_ID

IN (SELECT JOB_EXECUTION_ID FROM ${props.db.PREFIX}JOB_EXECUTION where CREATE_TIME < :date)

]]></value></property>

</bean>

<bean parent="itemWriterDef">

<property name="sql" ><value><![CDATA[

DELETE FROM ${props.db.PREFIX}JOB_EXECUTION where CREATE_TIME < :date

]]></value></property>

</bean>

<bean parent="itemWriterDef"><!-- ":date"は、必ずプレースホルダを1つは使用しないといけないので強引に使用している -->

<property name="sql"><value><![CDATA[

DELETE FROM ${props.db.PREFIX}JOB_INSTANCE WHERE JOB_INSTANCE_ID

NOT IN (SELECT JOB_INSTANCE_ID FROM ${props.db.PREFIX}JOB_EXECUTION)

AND :date = :date

]]></value></property>

</bean>

</list>

</property>

</bean>

</beans>

設定ファイル(cleaner.properties)

以下が設定ファイルです。設定ファイルはクラスパスの直下に置きます。

各項目の内容は名前から分かるかと思います。

DBの接続先などは変更して使用してください。

#cleaner properties example

#delete records after props.day months.

props.months=12

#DB settings

props.db.PREFIX=BATCH_

props.db.driverClassName=org.postgresql.Driver

props.db.url=jdbc:postgresql://localhost/sample

props.db.username=postgres

props.db.password=postgres

起動方法

以下のように起動します。一行で書きます。cleaner-context.xmlがあるディレクトリに移動してから実行してください。

java -Xmx512m -classpath ;lib\* org.springframework.batch.core.launch.support.CommandLineJobRunner classpath:cleaner-context.xml job1

ディレクトリ構成

ディレクトリ構成は以下のようにします。

┣ cleaner-context.xml

┣ cleaner.properties

┣ libフォルダ

┣ jarファイル(必要なものを全て置きます)

最後に

いかがでしたでしょうか?うまく動いたでしょうか?

XMLファイルの設定だけで、Javaのプログラムを一切記述することなくバッチを作ることができました。

楽しんでいただければ幸いです!

mavenで必要Jarをまとめた起動バッチzipを作る方法

参考までに、mavenで、Windowsで動作するbatと、Linux系で動作するシェルを作るeclipseプロジェクトを作りました。

もちろん、上記のクリーンアップ用のバッチを起動するシェルです。

batファイルをダブルクリックすれば上記のcleaner-context.xmlを実行します。

興味のある方はeclipseでインポートして試してみてください。

eclipseバッチ作成プロジェクトのダウンロード

【バッチzipを作る手順】

まず、環境変数にJAVA_HOMEを設定していない人は設定してください。

①eclipseに空のプロジェクト(Java Project)を作成し、上記ファイルをインポートする。

②プロジェクトを右クリック ⇒ Maven ⇒ Update Project...を選択します。

③eclipseのプロジェクトを右クリック ⇒ Run As ⇒ Maven build ...(ピリオド付き)を選択します。

Mainタブで、Goalsに「package」と記入して、Runボタンを押すとバッチを含むzipファイルを作成します。

ERRORになるようでしたら、もう一度「Run As」してみてください。

プロジェクトフォルダ内の「target」フォルダにzipが作成されますので確認してみてください。

2回目からは、プロジェクトを右クリック ⇒ Run As ⇒ Maven build(ピリオドなし)を選択すれば作成されます。

Created Date: 2016/11/02