Edited at

DBFlute 外だしsql使用せず、テーブル結合方法


はじめに

皆さん、はじめまして。本連載を担当する呉(WU)と申します。

代表的な3つのO/Rマッピングツール(iBATIS、Torque、Hibernate)がありますが、

今回はO/RマッピングとしてのDBFluteを紹介させていただきます。

※O/Rマッピングとは、「オブジェクト」と「リレーショナルデータベース」をマッピング(対応付け)することです。

なぜ、DBFluteの外だしSQLの利用をなるべく避けたい?

以下2点の理由:

①SQL文がソースコードの様々な場所に記述されていると、修正漏れや修正間違いといった問題を常に意識しながら開発しなければなりません。

②O/Rマッピングは実装時のデータベース操作にかかわる煩雑な作業を軽減し、「ミスマッチ」を解決してくれます。


課題背景

DBFluteのホームページがありましたが、知識がバラバラに点在し、現場の仕事を利用するためには、設定ファイル(additionalforeignkey.dfprop)とJavaソースの書き方を整理しなければならないです。


テーブル結合方法説明

この記事ではDBFlute-1.0.5NとJAVA7を使っています。


DBFluteの基本仕組み

DBFluteのO/Rマッピングの大まかな仕組み

(1)DBFluteにおける自動生成

DBFluteは、自動生成されたクラスがあって初めて動作するO/Rマッピングです。

(2)JAVA実装

  Behavior 全てのDBアクセスの処理を司る

  ConditionBean 検索条件を組み立てる

詳細は実装マニュアルをご参照ください。


2つ以上のテーブル間が外部キーも紐付けていない、業務上が結合したい場合には以下の方法で対応します。

例:


利用テーブル

TableA
TableB
TableC

A_column_1
B_column_1
C_column_1

A_column_2
B_column_1
C_column_1

※上記はTableAとTableBとTableCがすべて外部キーが紐付けていないことが想定する。


 下記の設定ファイルはdbflute/XXX/dfprop/additionalforeignkey.dfpropを指すこと。


1、期待実行結果SQL(TableAとTableB結合):


SELECT *

FROM TableA A

LEFT JOIN TableB B

ON A.A_column_1 = B.B_column_1

設定ファイル:


; FK_TableA_TO_TableB_BY_ID = map:{

; localTableName = TableA ; foreignTableName = TableB

; localColumnName = A_column_1 ; foreignColumnName = B_column_1

; fixedSuffix = ByID

}

JAVA:

//TableA情報の取得

TableACB cb = tableABhv.newMyConditionBean();

cb.setupSelect_TableBById();


2、期待実行結果SQL(TableA・TableBとTableB・TableC結合):


SELECT *

FROM TableA A

INNER JOIN TableB B

ON A.A_column_1 = B.B_column_1

INNER JOIN TableC C

ON B.B_column_1 = C.C_column_1

設定ファイル:


; FK_TableB_TO_TableC_BY_ID = map:{

; localTableName = TableB ; foreignTableName = TableC

; localColumnName = B_column_1 ; foreignColumnName = C_column_1

; fixedSuffix = ById

}

; FK_TableA_TO_TableB_BY_ID = map:{

; localTableName = TableA ; foreignTableName = TableB

; localColumnName = A_column_1 ; foreignColumnName = B_column_1

; fixedSuffix = ById

}

JAVA:

//TableA情報の取得

TableACB cb = tableABhv.newMyConditionBean();

cb.setupSelect_TableBById();

cb.setupSelect_TableBById().join();

cb.setupSelect_TableBById().withTableCBId();

cb.setupSelect_TableBById().withTableCBId().join();


3、期待実行結果SQL(TableA・TableBとTableB・TableCとTableA・TableC結合):

SELECT *

FROM TableA A

LEFT JOIN TableB B

ON A.A_column_1 = B.B_column_1

LEFT JOIN TableC C

ON B.B_column_1 = C.C_column_1

AND A.A_column_2 = C.C_column_2

設定ファイル:


; FK_TableA_TO_TableB_BY_ID = map:{

; localTableName = TableA ; foreignTableName = TableB

; localColumnName = A_column_1 ; foreignColumnName = B_column_1

; fixedSuffix = ById

}

; FK_TableA_TO_TableC_BY_ID = map:{

; localTableName = TableA ; foreignTableName = TableC

; localColumnName = A_column_2 ; foreignColumnName = C_column_2

; fixedCondition = $$foreignAlias$$.C_column_1 = $$over($localTable.tableBById)$$.B_column_1

; fixedSuffix = ById

}

JAVA:

//TableA情報の取得

TableACB cb = tableABhv.newMyConditionBean();

cb.setupSelect_TableBById();

cb.setupSelect_TableCById();


4、実行結果SQL(fixedConditionで相関サブクエリ):

SELECT *

FROM TableA A

INNER JOIN TableB B

ON A.A_column_1 = B.B_column_1

INNER JOIN TableC C

ON B.B_column_1 = C.C_column_1

AND A.A_column_2 = C.C_column_2

INNER JOIN

(SELECT TC.C_column_1

,MAX(CAST(TC.C_column_2 AS INT)) AS VER

FROM TableC TC

GROUP BY

TC.C_column_1

TC.C_column_2

) C_0

ON C_0.C_column_1 = C.C_column_1

設定ファイル:

; FK_TableA_TO_TableB_BY_ID = map:{

; localTableName = TableA ; foreignTableName = TableB

; localColumnName = A_column_1 ; foreignColumnName = B_column_1

; fixedSuffix = ById

}

; FK_TableA_TO_TableC_BY_ID = map:{

; localTableName = TableA ; foreignTableName = TableC

; localColumnName = A_column_2 ; foreignColumnName = C_column_2

; fixedCondition = $$foreignAlias$$.C_column_1 = $$over($localTable.tableBById)$$.B_column_1

INNER JOIN

(SELECT TC.C_column_1

,MAX(CAST(TC.C_column_2 AS INT)) AS VER

FROM TableC TC

GROUP BY

TC.C_column_1

TC.C_column_2

) C_0

ON C_0.C_column_1 = $$foreignAlias$$.C_column_1

; fixedSuffix = ById

}

JAVA:

//TableA情報の取得

TableACB cb = tableABhv.newMyConditionBean();

cb.setupSelect_TableBById();

cb.setupSelect_TableBById().join();

cb.setupSelect_TableCById();

cb.setupSelect_TableCById().join();



設定ファイルの用語説明

上記に表したマックは以下の意味合いで、理解した上で活用していくと思います。

$$foreignAlias$$:参照先テーブルのエリアス

$$localAlias$$:参照元テーブルのエリアス

$$over([テーブル名].[リレーション名])$$:別リレーションのテーブルのエリアス

$$over($localTable.[リレーション名])$$:LocalテーブルのForeignテーブル

$$over($foreignTable.[リレーション名])$$:ForeignテーブルのForeignテーブル



まとめ

BDfluteの設定ファイルとJAVAソースをペアーで組み合わせると、外だしsql使用せず、すべてのテーブル結合が可能です。

今回の説明は現場よく利用している複数テーブルの結合する場合に対応方法ですが、実際にはまたいろいろ細かいのノウハウと思っていますが、次回はJAVA側の実装するノウハウを説明します。



参考資料

~現場指向O/Rマッパー~

http://dbflute.seasar.org/

additionalForeignKeyMapとは?

http://dbflute.seasar.org/ja/manual/reference/dfprop/additionalforeignkey/