複数プロジェクトでのGenerateについて

59 views
Skip to first unread message

N.Miku

unread,
Jul 31, 2023, 11:21:48 AM7/31/23
to DBFluteユーザの集い
初めまして。
SpringBootのマルチプロジェクトでDBFluteを導入しているのですが、構成について質問したいことがございます。

下記のような3つのプロジェクトがあるとします。
・projectA
・projectB
・projectAとprojectBで共通的に使用する資材を配置するprojectC

使用したいテーブルが下記の通り3つあるとします。
・table_a(projectAでのみ使用)
・table_b(projectBでのみ使用)
・table_c(projectAとprojectBの両方で使用)

BehaviorやEntity等の自動生成クラスを生成する際に下記のように割り振りをする構成にしたいと考えています。
・table_aに関する資材をprojectAに配置
・table_bに関する資材をprojectBに配置
・table_cに関する資材をprojectCに配置

DBFluteでこういった構成は実現可能でしょうか。
また、実現可能な場合は設定方法をご教示いただけますでしょうか。

kubo

unread,
Jul 31, 2023, 11:56:31 AM7/31/23
to dbf...@googlegroups.com
jfluteです。こんばんは、N.Mikuさん。

SpringBootプロジェクトでのDBFlute導入ありがとうございます。

まずは前提の確認をさせてください。
そして、それぞれのケースケースで想定して実現可能性を追記していきますね。


project構成は以下のようなイメージで合ってますでしょうか?

|-projectA
|  |-src/main/java  // table_aとtable_cを使う
|  |          |-xxx
|  |          |  |-ProjectAApplication.java // main()でSpringApplication.run()する
|
|-projectB
|  |-src/main/java  // table_bとtable_cを使う
|  |          |-xxx
|  |          |  |-ProjectBApplication.java // main()でSpringApplication.run()する
|
|-projectC
|  |-src/main/java  // table_cを扱うクラスはこちらに置く
|  |          |- (AとBの共通ライブラリなのでrunは存在しない)
|  |
|  |-dbflute_xxxdb // たぶんDBFluteクライアントもここにある?
|  |-mydbflute // DBFluteエンジンもここ?



(↑上記のイメージだと仮定して)
そして、やりたいこととしては...

通常だと projectC にDBFluteの自動生成クラスがすべて生成されて、
projectAもprojectBもどのテーブルを使うことができるという感じになるところを...


projectAしか使わないテーブルの自動生成クラスはprojectAに生成、
projectBしか使わ...(同じ)


つまり、間違って projectA が table_b を使ってしまうとかがないように、
コンパイルレベルでブロックしたいということになりますでしょうか?



(↑上記のイメージだと仮定して)
今まで経験した現場だと、多少projectごとに「このテーブル使う使わない」がありますが、
「どれがどれを使う使わないを明確にして洗い出すのも大変」でしょうし、
「いま使ってなくてもいつ使うかわからない」ということもあり、
厳密に防ぐという話になることはありませんでした。


また、テーブル間でリレーションシップを持ってると、
テーブル間のEntityで相互参照が必要になるので、
自動生成先を分離するのはコンパイル言語として難しくなります。

例えば、table_a が table_c をFK参照している場合、TableA entity が TableC entity を参照します。
そこは大丈夫にしても、TableC entity も TableA entity をone-to-manyとして参照するので、
同じプロジェクトにいないとコンパイルを通すことができません。

その場合、コンパイルレベルでのブロックは諦めて、
アプリ実行時に「projectAがtable_bのBehaviorを使ったエラー」
というような実行時レベルチェックで割り切るとかの方が現実的かなとは思いました。
(これは比較的簡単に実装できるとは思います。実装方法は一旦置いておいて)



(↑リレーションシップの切れ目で綺麗に分けられる場合)
例えば、table_aグループ, table_bグループ, table_cグループという単位で、
同じDBスキーマ上でもリレーションシップ的な関わりが一切存在しないとかであれば...

DBFluteクライアントをprojectA, projectB, projectCごとに分けて運用するという方法も
できなくはないかもです。(やったことはないのでまだ想像ですが...)

ちょっと自動生成のライフサイクルなどが面倒になるかもしれないのと、
ConnectionPoolやTransactionどうする?という面がまたややこしいかもしれません。

なので、切れ目で綺麗に分けられる場合でも、
実行時レベルチェックの方が現実的だとは思います。

ただ、プロジェクト規模がものすごく大きくて、
コンパイルレベルでの制御に費用対効果が期待できるなら分割もアリだとは思います。



(↑そもそもそれぞれスキーマごとに違う場合)
例えば、table_aグループのテーブルと他のグループのテーブルが、
同じDBスキーマではなく、別のDBスキーマとかであれば、

どちらかというとDBFluteはスキーマごとにDBFluteクライアントを作成する、
(スキーマごとに自動生成をする)というスタイルになっていますので、
先述のDBFluteクライアントをプロジェクトごとに分けるやり方で良いと思います。



「あともうちょいこういう機能があったら実現できるのに」ってなったら、
今のDBFluteを修正することもできますので、ぜひ問題の整理をさせて頂ければと。

N.Miku

unread,
Aug 1, 2023, 3:29:06 AM8/1/23
to DBFluteユーザの集い
jfluteさん

早速のご回答ありがとうございます。

>  project構成は以下のようなイメージで合ってますでしょうか?
状況説明が不足していて申し訳ございません。
正におっしゃるとおりの構成です。

今回の質問をした背景です。
以下の事情により前述のproject構成となりました。

①以前はprojectAだけDBFluteを導入していて、その他のプロジェクトは別のO/Rマッパーを使用していた。
②projectAとprojectBは同一DBスキーマを共用している。
③projectAのリニューアルに伴いO/RマッパーをDBFluteで統一することになった。
④DBFluteの二重管理が嫌なのでprojectCにDBFluteクライアントを配置。

ところが、元々projectAの運用をしていたメンバーから
「projectAで使用しないテーブルの資材も読み込むことになったせいでビルド時間・アプリ起動時間が遅くなった」
といった意見がありました。
この意見を解消しつつ、二重管理も避ける良いやり方がないか確認をしたかった、という状況です。



> また、テーブル間でリレーションシップを持ってると、
> テーブル間のEntityで相互参照が必要になるので、
> 自動生成先を分離するのはコンパイル言語として難しくなります。

> 例えば、table_a が table_c をFK参照している場合、TableA entity が TableC entity を参照します。
> そこは大丈夫にしても、TableC entity も TableA entity をone-to-manyとして参照するので、
> 同じプロジェクトにいないとコンパイルを通すことができません。

この事情があるので難しそうだなと思いました。

2023年8月1日火曜日 0:56:31 UTC+9 jflute:

kubo

unread,
Aug 1, 2023, 7:46:17 AM8/1/23
to dbf...@googlegroups.com
jfluteです。

まず第一に、リニューアルのO/Rマッパー統一でDBFluteを選んで頂き、
とてもとても嬉しい限りで感謝の気持ちを述べさせてください。
本当にありがとうございます!!!

ってことなので、なんとか快適な形でDBFluteを使ってもらいたいところではありますが...
間違って使うのを防ぐとじゃなく、ビルド時間とかの話なのですね。


コンパイルセーフを徹底するDBFluteの特性上、
どうしてもスキーマ単位で同じコンパイル世界にいないといけないので、
ビルドレベルでクラスを取捨選択するのは難しいです。

かといって、二重管理は後々すれ違いミスを生みやすいのでオススメではないです。
(しっかりと運用のルール決めとか手順書作成とか同期の自動チェックとかできるなら悪くはないですが)


ここは一つ、「本当にごめんなさい」というところですね。

「作者に聞いてたら "ごめんなさい" だって」と伝えて頂ければというところですm(_ _)m。
(これは文句言われるのはしょうがないことで)


一方で、ビルド時間というところで言うと、改善できる要素が幾つかあります。


1. ConditionBeanのメソッドの削減

conditionBeanMap.dfpropにて、ConditionBeanの条件メソッドの自動生成を抑える機能があります。

// conditionBeanMap | DBFlute
https://dbflute.seasar.org/ja/manual/reference/dfprop/includequery/index.html

例えば...

o 文字列型カラムで大なり小なり "<", ">" 条件することがない
o 数値型カラムや日付型カラムでNotEqualすることはない

など、文法的にはできても業務的にはまずやることがない組み合わせとかもあるかと思います。
DBFluteのデフォルトでは、やれるものはほとんど自動生成するという感じですが、
conditionBeanMapでそれを制御することができるというところです。

別フレームワークになりますが、LastaFluteではExampleのデフォルトでかなりガッツリ抑制するようにしています。

// LastaFluteのExampleのconditionBeanMap | Github
https://github.com/lastaflute/lastaflute-example-harbor/blob/master/dbflute_maihamadb/dfprop/conditionBeanMap.dfprop

「要らないから削除する」というきっかけだけでなく、
「こういう条件やってほしくない」というきっかけでも使えるものです。

(自分なんかだと文字列型カラムで大なり小なりはDB設計的に抑制したいとかあったり)

こちらは、気軽に試すことができます。
再自動生成してみてコンパイルエラーにならなければOKということなので。

まあ、もちろんこれで劇的に改善するわけではなく、「チリも積もれば」という感じではありますが。



2. テーブル区分値のスーパースリム化

もし、テーブル区分値が多い場合は、テーブル区分値のスーパースリム化というのもあります。

// e.g. テーブル区分値のスーパースリム化 @since 1.0.5G
https://dbflute.seasar.org/ja/manual/reference/dfprop/includequery/index.html#tablecls

BehaviorクラスやCIQクラスなどの自動生成を抑制できるので効果は大きいです。


※ただし、DB設計的に「暗黙の区分値」がメインだとあまり関係ないです。



3. DBFlute-1.2.6でCDefがスリムに

DBFlute-1.2.6にて、CDefクラスのコード量がだいぶ減ってスリムになりました。
些細なことですが、新しいDBFluteのバージョンだと少しずつ工夫してコード量を減らすとかやってますので、
できるときにぜひ最新にアップグレードしてみてください。



4. lombokで除外設定(できるか?)

lombokを使われてるようであれば、それがビルド時間に影響している可能性もあります。

// Lombokの除外設定にDBFluteの自動生成クラスを
https://jflute.hatenadiary.jp/entry/20160822/lombokcost

ブログの通り、DBFlute自体はlombok使って無くても、
Lombokが探しに行ってしまうことで遅くなってしまいます。

自分はlombokが詳しくないので知らないのですが、
もしlombokの探索範囲の除外設定などがあったら設定すると良いです。

(除外設定ないのかなぁ...)



5. ComponentScanの範囲を微調整

一方で、Springの起動時間はなかなかどうにもなんですよね。
lombokと似た感じで、ComponentScanが走るのでたくさん見に行ってしまうんですよね。
(SpringMVCはあまり遅延ロードとかないから、起動時にほとんどのものを初期化されてしまうはずで)


ということで、DBFluteのSpringBoot exampleでは、
ComponentScanの範囲からDBFluteパッケージを外すようにしています。
(厳密には、exbhvだけが探索対象になるように)
(DBFluteのクラスとアプリのクラスが混ざらないようなパッケージ構成にしてることが前提で)

// dbflute-example-on-springbootの入り口のComponentScan
https://github.com/dbflute-example/dbflute-example-on-springboot/blob/master/src/main/java/org/docksidestage/app/application/ExampleApplication.java#L29

@SpringBootApplication
@ComponentScan("org.docksidestage.app")
@Import(DBFluteBeansJavaConfig.class)
public class ExampleApplication {

※これでどれだけ変わったかって検証はできてないですが...



ひとまず、思い付くところではこのような感じです。
もし、実際に「これやってみよう」となったら、1とか2とかはさらに詳しく説明しますので。
Reply all
Reply to author
Forward
0 new messages