ITお絵かき修行

3歩歩いても忘れないために

jBatch(Batchlet & Chunk)+JPA2.1(Entity & JPQL)でデータ読み書き

JavaEE7で追加されたjBatch(JSR-352)とJPA2.1(JSR-338)を使用して、DBのデータを読み書きする。

【やること】
・jBatchのBatchlet方式とChunk方式を両方使ってバッチジョブを実行する。
JPAのうち、SELECTはJPQL、INSERT,UPDATE,DELETEはEntity方式を使用してデータの読み書きをする。
処理概要は下図の通り。
f:id:hhhhhskw:20150128203403p:plain


【環境】
Java
 ・jdk1.8.0_25
 ・Oracle Enterprise Pack for Eclipse (12.1.3.3.1) ※中身はLuna SR1 (4.4.1)
 ・GlassFish Server Open Source Edition 4.1
JPA
 ・Eclipselink 2.5.2-RC1 ※GlassFish組み込み
●DB・JDBC
 ・PostgreSQL 9.0
 ・JDBC41 Postgresql Driver, Version 9.3-1102
ツール
 ・pgAdminⅢ


【準備】
JDBCGlassfishのクラスパスが通る場所(libフォルダ)に置いた。
・srcフォルダ配下にMETA-INFフォルダを作り、その中に「batch-jobs」フォルダを作った。
GlassfishにてJNDIデータソースを設定済。
 [参考]GlassfishでJNDIデータソースの設定方法その1 - しんさんの出張所 はてな編
・データ読み書き用のテーブルを作成済。

CREATE TABLE employees
(
  empno integer NOT NULL,
  ename character varying(10),
  yomi character varying(20),
  job character varying(8),
  mgr integer,
  hiredate date,
  sal character varying(7),
  comm character varying(7),
  deptno integer,
  CONSTRAINT pk_emp PRIMARY KEY (empno)
)
WITH (
  OIDS=FALSE
);



【資産 - 定義体
1.emp-batch.xml
・バッチジョブの実行順序を記述

2.persistence.xml
JPA定義+DB接続先情報
・"eclipselink.logging.level.sql"を"FINE"とすると生成されたSQLが見える


【資産 - ソースファイル】
1.InsertEmpInfoBatchlet.java
・データ入力役
・従業員番号2001番・太郎くんの情報をINSERTする。 ※入力データはハードコーディングした。

2.EmpInfoReader.java
・データ読み込み役
・従業員番号2001番・太郎くんの情報をSELECTする。
・1件取得する場合はTypedQuery#getSingleResultを使用する。

3.EmpInfoProcessor.java
・データ加工役
・SELECTしたデータに数値を設定する。入力データに意味はない

4.EmpInfoWriter.java
・データ書き込み役
・EmpInfoProcessorで加工したデータを適用し、UPDATEする。

5.DeleteEmpInfoBatchlet.java
・データ消去役
・従業員番号2001番・太郎くんの情報をDELETEする。

6.BatchServlet.java
・バッチジョブを実行する。


作成したプロジェクトのトップページは以下。
hhhhhskw/EmpInfoBatch · GitHub


【実行ログ】

2015-02-11T15:07:09.847+0900|情報: EmpInfoBatch was successfully deployed in 5,035 milliseconds.
2015-02-11T15:07:26.098+0900|情報: id = 50
2015-02-11T15:07:26.271+0900|情報: InsertEmpInfoBatchlet#process
2015-02-11T15:07:26.393+0900|普通: INSERT INTO employee (empno, comm, deptno, ename, hiredate, job, mgr, sal, yomi) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
	bind => [9 parameters bound]
2015-02-11T15:07:26.493+0900|情報: JTS5014: Recoverable JTS instance, serverId = [100]
2015-02-11T15:07:26.572+0900|情報: EmpInfoReader#open
2015-02-11T15:07:26.574+0900|情報: EmpInfoWriter#open
2015-02-11T15:07:26.578+0900|情報: EmpInfoReader#readItem
2015-02-11T15:07:26.770+0900|情報: EmpInfoProcessor#processItem
2015-02-11T15:07:26.772+0900|情報: EmpInfoWriter#writeItems
2015-02-11T15:07:26.772+0900|情報: persistEmployee
2015-02-11T15:07:26.772+0900|情報: EmpInfoReader#checkpointInfo
2015-02-11T15:07:26.783+0900|情報: EmpInfoWriter#checkpointInfo
2015-02-11T15:07:26.791+0900|普通: UPDATE employee SET comm = ? WHERE (empno = ?)
	bind => [2 parameters bound]
2015-02-11T15:07:26.823+0900|情報: EmpInfoReader#readItem
2015-02-11T15:07:26.824+0900|情報: EmpInfoReader#checkpointInfo
2015-02-11T15:07:26.835+0900|情報: EmpInfoWriter#checkpointInfo
2015-02-11T15:07:26.842+0900|情報: EmpInfoReader#close
2015-02-11T15:07:26.843+0900|情報: EmpInfoWriter#close
2015-02-11T15:07:26.886+0900|情報: DeleteEmpInfoBatchlet#process#begin
2015-02-11T15:07:26.906+0900|普通: UPDATE employee SET comm = ?, deptno = ?, ename = ?, hiredate = ?, job = ?, mgr = ?, sal = ?, yomi = ? WHERE (empno = ?)
	bind => [9 parameters bound]
2015-02-11T15:07:26.926+0900|普通: DELETE FROM employee WHERE (empno = ?)
	bind => [1 parameter bound]
2015-02-11T15:07:26.926+0900|情報: DeleteEmpInfoBatchlet#process#commit




【つまったところなど】
バッチ処理で無限ループした時はItemReaderの実装をまず疑う。
→readItemメソッドの戻り値にnullが設定されるコードになってるか、チェックポイントをカウントする実装はおかしくないかetc
・JPQL使用時のコードでPSQLExceptionが発生する場合は、9割方文法が間違っている(体感)。
→SELECT句のカラム名に「*」を設定していないか、FROM句にEntityクラス名を指定しているかetc
EclipseからGlassfishへ資産をデプロイできない場合は、EclipseからGlassfishサーバーを一旦削除して、Glassfishサーバーを再度作成する。もしくはドメインフォルダ配下に生成された一時フォルダ・ファイルを削除する。
・それでもデプロイできないときは、Eclipseと端末の再起動
Glassfish起動時にポート1527が開いていません的例外が出たら、JDK付属のJavaDBを起動したうえで、Glassfishを起動する。

[2015-01-27T23:52:11.436+0900] [glassfish 4.1] [WARNING] [poolmgr.create_resource_error] [javax.enterprise.resource.resourceadapter.com.sun.enterprise.resource.allocator] [tid: _ThreadID=47 _ThreadName=admin-listener(5)] [timeMillis: 1422370331436] [levelValue: 900] [[
  RAR5038:Unexpected exception while creating resource for pool DerbyPool. Exception : javax.resource.spi.ResourceAllocationException: Connection could not be allocated because: java.net.ConnectException: ポート1527のサーバーlocalhostへの接続中にメッセージConnection refused: connectでエラーが発生しました。]]



【参考文献・URL】
The Java EE 7 TutorialのjBatchの章をテキトーに訳した - kagamihogeの日記
jBatchでデータロードしてみる - kagamihogeの日記
jbatch (JSR352) - Chunk方式のStepを使ってみる - @lbtc_xxx lab
Java EE6を使う
GitHubに間違ってあげてしまった過去のコミットを削除してみる|めっとぼ

Beginning Java EE 6 GlassFish 3で始めるエンタープライズJava (Programmer’s SELECTION)

Beginning Java EE 6 GlassFish 3で始めるエンタープライズJava (Programmer’s SELECTION)