Asakusa FrameworkでOracleシーケンスを使う例。
Asakusa Framework 0.10.1でOracleシーケンスを使って一意の値を採番するサンプルを作ってみた。
AsakusaFWでRDBにアクセスする場合は、普通はWindGate JDBCを使う。
しかしWindGateはバッチの最初にテーブルから読み込む・バッチの終わりにテーブルにINSERTする機能であり、バッチの途中でJDBCアクセスすることは出来ない。
しかし、AsakusaアプリもJavaアプリである為、直接JDBCを使う事は出来る。
(JDBC接続情報はWindGateプロファイルのものを使いたかったが、バッチ本体の実行時にはプロファイルにアクセスする手段が無いようなので、core/conf/asakusa-resources.xmlに記述している)
当サンプルでOperatorからシーケンスを呼ぶには以下のようにする。
import com.example.api.sequence.SequenceApi;
public abstract class SequenceExampleOperator { @Update public void updateSequenceNumber(SeqExample in) { long number = SequenceApi.nextLong("seqTest"); in.setSeqNumber(number); } }
"seqTest"
は、シーケンスの設定の名称。
これにより、asakusa-resources.xmlのsequence.seqTest.class
に指定されたクラスを使ってシーケンスを取得する。
本番環境(実際にOracle DBにアクセスする)用の設定の例。
<property> <name>sequence.seqTest.class</name> <value>com.example.api.sequence.OracleSequenceDelegate</value> </property> <property> <name>sequence.seqTest.url</name> <value>jdbc:oracle:thin:@DB-SERVER:1521:ORCL</value> </property> <property> <name>sequence.seqTest.user</name> <value>asakusa</value> </property> <property> <name>sequence.seqTest.password</name> <value>asakusa</value> </property> <property> <!-- Oracleシーケンス名 --> <name>sequence.seqTest.sequenceName</name> <value>SEQTEST1</value> </property>
ojdbc.jarはsrc/main/libs/に置いておく。(Gradleでライブラリーを置く為の、標準的な方法)
これで、デプロイメントアーカイブ作成時に各バッチのlibにjarファイルが配置される。
また、build.gradleのdependenciesにasakusa-bridge-runtimeを入れてある。
これが無いと、バッチコンパイルが通らない。
dependencies { compile group: 'com.asakusafw.bridge', name: 'asakusa-bridge-runtime', version: asakusafw.vanilla.version }
ちなみに、バージョンにはasakusafw.vanilla.versionが指定してある。
実際の値は、AsakusaFW
0.10.1の場合は0.5.1になる。この値がAsakusaFWのバージョン毎にどういう値になるのか分からないので^^;、Vanillaのバージョンで代用している。
フローのテスト用の実行環境の設定例。
<property>
<name>sequence.seqTest.class</name>
<value>com.example.api.sequence.LocalSequenceDelegate</value>
</property>
LocalSequenceDelegateは(実際のRDBを使用せずに)AtomicLongを使い、連番を返す。
これを使えば、テスト環境にRDBを用意する必要が無い。
サンプルでは、src/dist/devにこの設定を記述し、build.gradleの個別環境用設定でsrc/dist/devを使うように記述してある。
このため、ShafuでAsakusa
Frameworkのインストールを行うと、この設定が入ったテスト実行環境が作られる。
OperatorからSequenceApiを呼び出すと、内部ではSequenceAdapterを使ってSequenceDelegateを取得する。
SequenceDelegateは「シーケンスの設定の名称」およびスレッド毎に個別のインスタンス。
SequenceAdapter.delegateメソッドは以下の様な感じになっている。
import com.asakusafw.bridge.broker.ResourceBroker; import com.asakusafw.bridge.broker.ResourceCacheStorage; import com.asakusafw.runtime.core.ResourceConfiguration;
private static final ResourceCacheStorage<SequenceAdapter> CACHE = new ResourceCacheStorage<>();
private static final Supplier<SequenceAdapter> SUPPLIER = () -> { ResourceConfiguration conf = ResourceBroker.find(ResourceConfiguration.class); if (conf == null) { throw new AssertionError("not found ResourceConfiguration"); } SequenceAdapter adapter = new SequenceAdapter(conf); ResourceBroker.schedule(adapter); return adapter; };
// スレッド毎に異なるインスタンスを返すので、戻り値をフィールドに保持しないこと public static SequenceDelegate delegate(String name) { SequenceAdapter adapter = CACHE.get(SUPPLIER); return adapter.getDelegate(name); }
ResourceBrokerは、クラス毎にインスタンスを生成・保持するクラス。
ここにscheduleメソッドでインスタンス(Closeable)を登録しておくと、バッチ終了時にcloseメソッドが呼ばれる。[/2018-11-01]
今回は、ここでJDBCのConnection等のクローズを行っている。
ResourceCacheStorageは、スレッド毎にインスタンスを生成・保持してくれるクラス。
(要するにThreadLocal)
ちなみに、ResourceConfigurationはasakusa-resources.xmlの内容を保持している。
Oracleシーケンスの場合、採番(値の更新)は、通常のトランザクションとは独立して実行される。
したがって、今回のサンプルでは、JDBC Connectionのトランザクションは特に使用(設定)していない。