しおしお

IntelliJ IDEAのことなんかを書いてます

JSR352-Batch Applicationを試してみた(BatchletでDBアクセス-JPA編)

JSR352-Batch Applicationを試してみた(Batchlet編) - しおしお(´・ω・`)の続編です。

今回は、Batchletでデータベースにアクセスしてみようかなと。データベースへのアクセス方法は、JPA使ってもいいし、素のJDBCオープンソースのライブラリを使ってもいいでしょう。

今回は、JPAを使ってみます。JPAを使った場合、Batchletの実装ではJTAトランザクションが開始されていないので、Batchretで新しいトランザクションを開始して上げる必要があります。

※Bachletの処理は下の流れで行われると仕様に書かれているので、Batchletでデータベースアクセスするのってあんまり想定されていないんかなと思ったり。でも、ガツンと一発で処理したいときはBatchletでやりたいしなぁと

11.4 Regular Batchlet Processsing

  1. <Create StepContext>
  2. <Store step level properties in StepContext>
  3. <->[StepListener.beforeStep...] // thread A
  4. <->Batchlet.process // thread A
  5. // if stop issued:
  6. <->[Batchlet.stop] // thread B, StepContext is available
  7. <->[StepListener.afterStep...] // thread A
  8. <Store StepContext persistent area>
  9. <Destroy StepContext>

さっそく実装例を簡単に。。。

Batchlet実装

ジョブ定義で指定した数分のデータをJPAを使って登録しているだけのBatchletです。登録件数のフィールドがStringになっているのは、参照実装がBatchPropertyで数値を使えないので・・・。(JBeretはString以外の型も使いそうなのはサポートしてくれているのですがね。)

package siosio.batchlet;

import javax.batch.api.AbstractBatchlet;
import javax.batch.api.BatchProperty;
import javax.enterprise.context.Dependent;
import javax.inject.Inject;
import javax.inject.Named;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.transaction.Transactional;
import javax.transaction.Transactional.TxType;

import siosio.entity.UserEntity;

@Dependent
@Named
@Transactional(TxType.REQUIRES_NEW)
public class DbAccessBatchlet extends AbstractBatchlet {

    @PersistenceContext(unitName = "batch")
    EntityManager em;

    @Inject
    @BatchProperty
    String count;

    @Override
    public String process() throws Exception {
        final int insertCount = Integer.parseInt(count);

        for (int i = 0; i < insertCount; i++) {
            em.persist(new UserEntity("name_" + (i + 1)));
        }

        return "SUCCESS";
    }
}

Entityの実装

IDと属性を1つだけ持ったEntityを作っています。とりあえず、動かすことが目的なのでこの程度で十分でしょうと。

package siosio.entity;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.NamedQuery;
import javax.persistence.Table;

@Entity
@Table(name = "users")
public class UserEntity {

    private Long id;

    private String name;

    public UserEntity() {
    }

    public UserEntity(String name) {
        this.name = name;
    }

    @Id
    @GeneratedValue
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

ジョブ定義ファイル

上で作ったBatchletをステップに指定しています。countプロパティに50を指定しているので、50レコードが登録されます。

<job id="db-access-job" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0">
  <step id="clear-users">
    <batchlet ref="dbAccessBatchlet">
      <properties>
        <property name="count" value="50" />
      </properties>
    </batchlet>
  </step>
</job>

persistence.xml

特にこれといって何もありません。。。

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" version="2.1">

  <persistence-unit name="batch" transaction-type="JTA">
    <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
    <jta-data-source>jdbc/jbatch</jta-data-source>
    <exclude-unlisted-classes>false</exclude-unlisted-classes>
    <properties>
      <property name="javax.persistence.schema-generation.database.action" value="create" />
      <property name="eclipselink.logging.level" value="fine" />
    </properties>
  </persistence-unit>

</persistence>

実行結果の確認を・・・

分かりづらいけど、テーブルに50レコード入ったしちゃんと動いたんだろな感がある。

f:id:sioiri:20150607151221p:plain

ソースコード全量はこちら→siosio/jbatch-sample · GitHub


おわり。

JSR352-Batch Applicationを試してみた(Batchlet編)

JSR352(Batch Application)を動かして見たので、その記録を残しておくと。

まず、動かしてみたいレベルだったのでサクッと動かすことができそうなBatchletを使っています。
実際にバッチアプリケーションを作成する際は、BatchletにはSQL一発ですむ処理を実装したりするのだと思います。

早速コードなどなどを見ていきます。

ジョブ定義

ジョブ定義は、META-INF/batch-jobs配下にxmlファイルで作成する必要があります。
こんな感じですね。
f:id:sioiri:20150606004658p:plain

ファイルの内容は下のようになっています。
そんな難しい構造ではないのでさくっと作れます。jobのid属性には、xmlのファイル名の拡張子をなくしたものを設定します。
今回は、Batchletを動かすので、ステップ定義(step id="step"の部分)にbatchletを定義します。
ref属性には、cdiで管理されているBatchletの名前を設定します。ref属性には、FQCNでBatchletを指定できますが、CDIの管理対象にならないので@Injectが効かなかったりします。(JobContextやStepContextは特別扱いされているっぽくインジェクションされるようです。)

<job id="batchletSample" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0">
  <step id="step">
    <batchlet ref="helloWorldBatchlet" />
  </step>
</job>

Batchletの実装

Batchletの処理はprocessメソッドで行うので、とりあえず実行したジョブ名称をログ(標準出力)に出してみます。

Namedアノテーションをはっているので、ジョブxmlにはこのオブジェクトの名前を設定するだけで良くなります。(前述のジョブxmlのbatchlet部のように)

import javax.batch.api.AbstractBatchlet;
import javax.batch.runtime.context.JobContext;
import javax.enterprise.context.Dependent;
import javax.inject.Inject;
import javax.inject.Named;

@Named
@Dependent
public class HelloWorldBatchlet extends AbstractBatchlet {

    // ジョブ名称を取得するためにJobContextをインジェクション
    @Inject
    JobContext jobContext;

    @Override
    public String process() throws Exception {
        System.out.format("**************************************************%n"
                        + "  job name: %s%n  **************************************************",
                jobContext.getJobName());
        return "SUCCESS";
    }
}

バッチ実行用のAPI

jax-rs使って、指定されたバッチジョブを実行する感じにしています。kotlinで書いていますが、Javaで書いてもあまり変わらないと思います。
戻り値は、ジョブの実行を一意に識別できるExecutionIdとしています。このIDは、ジョブのリスタート時に指定したりするので割りと重要な値となっています。

ジョブの実行は、BatchRuntimeからJobOperatorを取得してstartを呼び出すだけです。
このstartメソッドには、ジョブ名称(ジョブxml拡張子を除いたもの)を指定します。2番めのパラメータにはバッチ引数を指定するのですが今回は不要なのでnullを渡しています。

@Path(value = "/")
open class BatchResource {

  @GET
  @Path(value = "/run/{jobName}")
  open fun start(PathParam("jobName") jobName: String): String {
    val jobOperator = BatchRuntime.getJobOperator()
    val executionId = jobOperator.start(jobName, null)
    return executionId.toString()
  }
}

実行してみよう

端折りますが、バッチ処理と実行用APIglassfishにデプロイして実行します。glassfishには、JSR352のRIのjBatchがのっています。SE環境でも実行することは可能ですが、面倒臭そうだったのでglassfish上で動かす方を試してみました。

実行は、IntelliJさんのRest Clientからやってみます。
f:id:sioiri:20150606010538p:plain

実行してみると、レスポンスとしてExecutionIdが戻ってきます
f:id:sioiri:20150606011031p:plain

Batchletで行っているジョブ出力の結果も確認できます。
f:id:sioiri:20150606011347p:plain

一応glassfishの管理コンソールからも実行結果を確認できます。
f:id:sioiri:20150606011235p:plain

思っていたよりサクッと動いた感があります。

ソースコード全量はこちら→siosio/jbatch-sample · GitHub


おわり。

IntelliJのGenerateメニューで生成されるアクセッサにJavadocも出力するように

IntelliJ14.1から、Generateメニューで生成するアクセッサのフォーマットがカスタマイズできるようになりました。
これで、旧バージョンでは出来なかったJavadocのカスタマイズなんかもできるようになるので、テンプレートの編集手順まとめてみました。

アクセッサを生成するためのGenerateメニューを開く

こんな感じに選択候補が出てくるので、どれか選びましょう
f:id:sioiri:20150219130048p:plain

生成されるコードのテンプレートを選択する

14.1からは、こんな感じにテンプレートを選択できるようになっています。
デフォルトだと、GetterはIntelliJ DefaultのみでSetterはIntelliJ DefaultとBuilderが選べます。が、これらのテンプレートはJavadocは生成してくれないので、おれおれテンプレートを作ってあげます。
f:id:sioiri:20150219130246p:plain

テンプレートの追加画面を開く

下の画像の手順でテンプレート編集画面を開きます。新規でテンプレート作るの無理なので、IntelliJ Defaultを選択してコピーしてあげましょう。
f:id:sioiri:20150219130536p:plain

テンプレートにJavadocを追加

画像の枠線の中のように好みのJavadocコメントを追加してあげます。フィールド名は、$field.nameで取ってこれます。($fieldが持ってるプロパティは、Ctrl + Spaceでリスト表示されます。)
f:id:sioiri:20150219130848p:plain

生成されたコード

テンプレート通りに生成してくれました。

    /**
     * get test.
     *
     * @return test
     */
    public String getTest() {
        return test;
    }

IntelliJでクラスのヘッダーコメントをプロジェクトごとカスタマイズしよう

@さんの下のツィートを見て、そういえば
IntelliJ IDEA 14.1 EAP is Available! | JetBrains IntelliJ IDEA Blogにファイルテンプレートをプロジェクトで持てるみたいなこと書いてあったなと思って調べてみた。

IntelliJ IDEA14.1のFile Templateの設定画面に行くと、右上にSchemaなるリストボックスが増えてる!!
これを、Projectにしてあげてテンプレートいじると、その設定はプロジェクトのみに有効となります!なのでauthorをプロジェクトごと変えるとか割りと簡単にできる!!

この画像で、選んでるFile HeaderがClassのJavadocになる部分です。
f:id:sioiri:20150210224505p:plain

14.0系の設定画面だと、下の画像のように右上のリストボックスがない!!残念な感じです。
f:id:sioiri:20150210223741p:plain

ちなみに、Generate -> Getter(Setter)の生成ちと変わってて、生成するものをメニューで選択すると、下のようなメニューが出るようになってた。
f:id:sioiri:20150210224312p:plain

アクセッサのテンプレート編集方法はこちら→
http://siosio.hatenablog.com/entry/2015/02/19/131405


おわり。

IntelliJ IDEA14でのコード補完を整理してみた

コード補完の設定(IntelliJ IDEA12版)へのアクセス未だにあるので、バージョン14でのコード補完どうなってるか整理してまとめてみた。

コード補完の設定画面

バージョン14から設定画面ガッツリ変わったので、Code Completionの設定する場所にたどり着くの毎回苦労する感あります。
f:id:sioiri:20150127225910p:plain

設定内容

Case sensitive completion
候補を絞り込むときの条件(どの程度厳密にするか)を選択する。

選択肢 内容
All 大文字小文字完全一致で候補が出るようになる。
f:id:sioiri:20150127234406p:plain
None 大文字小文字を区別せずに候補が出るようになる。
f:id:sioiri:20150127235341p:plain
First letter 最初の文字のみ大文字小文字を区別して候補を出します。
f:id:sioiri:20150127235556p:plain

私は、Shift押すのとか面倒なのでNoneを選択してる感じです。(一時期、変数とクラス名区別してくて、First letterを選択してたけど、わたしにはあまりあわなかった)

Auto-insert when only one choice on
候補が単一だった場合の振る舞いを選択する。チェックをONにすると選択候補が一つだけだった場合に、リストを表示せずに候補を確定状態にします。
これは、チェックONにしといたほうが良いですね。

Sort lookup items lexicographically
候補リストの表示順を設定します。チェックをONにすると辞書順に表示されます。

チェックON、OFFでの違いはこんな感じになります。これはどっちでもいいような気がします。(私は、チェックOFFにしてますが)

チェックをONにした場合
f:id:sioiri:20150128232201p:plain
チェックをOFFにした場合
f:id:sioiri:20150128232205p:plain

Autopopup code completion
自動で候補をリスト表示するかどうかを選択する。これは超絶便利なのでチェックしてあげるべきです。

Insert selected variant by typing dot, space, etc.
候補選択時に、ドットやスペースをタイプした場合にその候補を確定させるかどうかを選択する。

これは、超絶便利じゃないか!と思った時もあったけど、めっちゃストレス溜まるからチェックは外してます。私が使いこなせなかっただけかもだけど、お前じゃないんだよって候補が選択されることがあったので耐えられなくなりましたね。

Autopopup documentation
ドキュメント(Javadoc)を自動で表示するかを選択する。チェックした場合には、どれ位経過後に表示するかをミリ秒で入力する。
私は、見たくなった時に表示できればいいのでチェックはしていません。

Autopopup in
パラメータ情報を自動で表示するかどうかを選択する。チェックした場合には、どれ位経過後に表示するかをミリ秒で入力する。
私は、見たくなった時に表示できればいいのでチェックはしていません。

Show full signatures
パラメータ情報でどこまで表示するかを選択する。チェックすると、メソッド名や戻り値も表示されるようになる。私は長くなると見づらくなるのでチェックはしていないです。

IntelliJ IDEA12に頃のコード補完の設定画面は、こんな感じでした13からauto popupさせる時間の設定がなくなったりしてだいぶスッキリした感じがあります。
f:id:sioiri:20150127234535p:plain

いろいろなコード補完を使ってみよう

Basic Completion
最もよく使われる補完タイプだと思います。デフォルトだと、Ctrl + SpaceがBasicな補完タイプです。あとは、AutopopupなやつもBasicタイプだと思います。

こんな感じに、超絶曖昧な情報でもいい感じに候補を表示してくれてかなり頭いいかんじです。
f:id:sioiri:20150129222457p:plain
キャメルケースの各ワードの先頭文字で候補絞ってくたりもする。
f:id:sioiri:20150129222808p:plain

Basic Completionで候補表示時にもう一度Ctrl + Spaceで、その昔存在していたClass Name Completionぽい絞りこみが行われる。例えば、依存関係で参照可能なクラス名やstaticなフィールドやメソッドが候補として表示される。

Smart completion
そのスコープないで適切なもの(変数に代入可能なものやキャストで利用可能なクラスのみなど)のみを候補表示してくれる。デフォルトだと、Ctrl + Shift + SpaceがSmartな補完タイプです。
Basicと比べるとかなり頭良い感じです。

こんな感じに、Basicタイプで表示された候補がSmartタイプに切り替わることでかなり絞りこまれています。
f:id:sioiri:20150129225143g:plain

SmartタイプもBasicと同じように、2回モードが有りかなり賢い候補の表示がされます。
下の動画のように、最初のSmartタイプでは候補がないと言われるけど、さらにSmart補完を行うことでコンテキスト内で利用可能な変数やメソッドが持っているメソッドなどまでたどって候補を表示してくれます。
これめっちゃ便利なやつです。
f:id:sioiri:20150129225733g:plain

Smart2回タイプでは、配列→List変換の候補表示もしてくれたりする。
f:id:sioiri:20150129230407g:plain

Postfix Completion
変数名やメソッド名の後に.(ドット)とキーワードを入力することで、対応するテンプレートを展開してコード補完してくれるやつです。
例えばnullチェックの分岐テンプレートは、nullキワードで展開できます。
f:id:sioiri:20150129230959g:plain
利用可能なテンプレート一覧は、SettingsのEditor -> General -> Postfix Completionから確認できます。
割りと大量にあります。
f:id:sioiri:20150129231129p:plain

Words Completion
ファイル内の単語を補完してくれる。これは、コメント内や文字リテラル内のやつも候補に出してくれる。デフォルトだとAlt + Slashで利用できる。
この補完は、候補をリスト表示してくれるのではなく、Alt + Slashをのたびに次のワードを表示してくれる。

検索窓のワード補完
ファイルない検索の窓でCtrl + Spaceをしてあげるとそのファイル内のワードを候補で表示してくれる。ほとんど使ってないので便利さよくわかってない。
f:id:sioiri:20150129231716p:plain

まだまだ、いろいろありそうな気がするけど長くなったのでこのへんで終わりにする。

IntelliJでGradleプロジェクトを新規作成した時に出力されるbuild.gradleの内容を変更する

IntelliJでGradleプロジェクトを新規で作った場合、build.gradleが以下の内容で出力される。(Use default gradle wrapperを選んだ場合はwrapperタスクは出力されていないと思います。)

この内容だと、ソースコードの準拠レベルが1.5だったりjunitのバージョンが1つ古かったり(このブログを書いてる時点で)と残念なところがあるので好みの形にテンプレートを変更してあげるのがいいと思います。

apply plugin: 'java'

sourceCompatibility = 1.5
version = '1.0'

task wrapper(type: Wrapper) {
  gradleVersion = '1.9'
  distributionUrl = 'http://services.gradle.org/distributions/gradle-1.9-all.zip'
}

repositories {
    mavenCentral()
}

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.11'
}

テンプレートの変更方法

File and Code Templatesからテンプレートを変更できます。Gradleのテンプレートは2種類用意されていますが、プロジェクト作成時に「Use default gradle wrapper」を選択した場合には「Gradle Build Script」が使用されます。「Use customizable gradle wrapper」を選択した場合には「Gradle Build Script with Wrapper」が使用されます。
f:id:sioiri:20150121232835p:plain

こんな感じに自分用にテンプレートを変更してあげます。
f:id:sioiri:20150121233717p:plain

プロジェクトを新規で作ると、こんな感じにテンプレート通りにbuild.gradle作ってくれます。

apply plugin: 'java'

sourceCompatibility = 1.8
version = '1.0'

task wrapper(type: Wrapper) {
  gradleVersion = '2.1'
  distributionUrl = 'http://services.gradle.org/distributions/gradle-2.1-all.zip'
}

repositories {
  mavenCentral()
}

dependencies {
  testCompile 'junit:junit:4.12'
}

これで、毎回生成されたテンプレート修正するとかしなくて良くなる感じです。

おわり。

IntelliJのデバッグ実行でSpringBootのホットデプロイできちゃう感

SpringBoot+Gradleでホットデプロイを使えるようにを書いたところ、@さんから下の反応もらえた!

※SpringBoot系のエントリあげると、いつもアドバイスもらえるのでほんとにありがたい!

ということで、さっそくIntelliJ IDEAからデバッグ実行して試してみた!

デバッグ実行は、おなじみの[Run]->[Debug]からですね。デバッグ実行中にコード変更してコンパイルすると新しいクラス再ロードする?的なこと聞いてくるのでYes押しちゃいましょう!
f:id:sioiri:20150116235850p:plain

もし、ダイアログが表示されない&クラスリロードされない!場合には、設定でリロードしたくないって選択されているので修正しましょう。
設定箇所は、下の画像(IntelliJ IDEA14の設定画面の場合)を見てください。該当箇所がNeverになってるとダメなので変更してあげます。私はAsk派です。
f:id:sioiri:20150117000722p:plain

あとは、ブラウザでリロードしてあげましょう。いい感じに最新化されているはずです。

@さんもTwitterのリプライで書いてくれていますが、メタ情報を変更した場合はデバッグ実行でのリロードは効いてくれません。個人的には、デバッグ実行だと出来る範囲が限定されるので、前のエントリであげたspringloaded使っといたほうがいいかなと思ったりしてます。

おわり。