移行 1.0.x to 1.1
- お約束の注意点
- 概要
- 大前提
- DBFlute-1.1.xをセットアップ
- まず、互換モードを全開にする
- 互換のないコンパイルエラーを直す
- dfpropの移行
- もう少し攻める
- チャレンジな世界へ...はちょい待って
お約束の注意点
- 古いバージョンの削除
- 古いバージョンのDBFluteランタイム(JAR)が[WEB-INF/lib]の下などに残らないように
- タスクを実行し忘れないように
- Generateタスクだけでなく、(外だしSQLがある場合は)Sql2Entityタスクも実行
概要
1.0.x から 1.1 に移行するのは、かなりの強者(つわもの)です。
移行のレベルが大きく三つあります。
- ひとまず 1.1 にするだけ
- 互換モードを全開にして、できるだけ修正を少なく移行
- とにかく 1.1 に合わせる
- 互換モードをOFFにして、方式もすべて移行
- その中間くらい
- ひとまず 1.1 にして、簡単に移行できそうなものだけ移行
まずは、"ひとまず 1.1 にするだけ" を目標に、現実的に "その中間くらい" に短い時間でチャレンジしてキリのいいところで落ち着くのがよいでしょう。
互換モードも、すべての機能に対応しているわけではありません。 メジャーな機能は互換を保てるようにしていて、マイナー(と思われる)機能に関してはちょっと手を動かせば移行できるだろうということで、バランスをとっています。 また、互換を保っていない機能の多くが、コンパイルレベルで検知できるものになっています。
大前提
- Java8であること
- Java8でなければ、とりあえず1.0.xのままJava8へ移行してから
- 1.0.xの最新であること
- 1.0.xの最新版でなければ、まずは最新版へ移行してから
- テストすること
- 特に運用中のシステムなら、しっかりシナリオテストすること
- 聞くこと
- わからなければ、ML や Twitter などで遠慮なく jflute に聞くこと
DBFlute-1.1.xをセットアップ
DBFluteエンジンのダウンロード
1.1.xのDBFluteエンジンをダウンロードします。
manageタスクの upgrade (94) をバージョン指定で実行します。 Windowsであっても必ずコマンドラインから実行する必要があります。
e.g. manage にて、バージョン指定で upgrade @Command
// (1.1.xの最新版バージョンを指定しましょう)
...$ sh manage.sh 94 1.2.0
この時点では、自動生成はまだやりません。
DBFluteランタイムの準備
1.1.xのDBFluteランタイムを用意します。
e.g. DBFluteランタイム-1.1.0のdependencyを定義 @pom.xml
// (1.1.xの最新版バージョンを指定しましょう)
<dependencies>
...
<dependency>
<groupId>org.dbflute</groupId>
<artifactId>dbflute-runtime</artifactId>
<version>1.2.0</version>
</dependency>
...
</dependencies>
この時点で、ことごとくコンパイルエラーになるはずです。
Log4jを使っていたらSlf4jのブリッジを
DBFlute-1.1.0から、DBFlute内部では commons-logging ではなく slf4j を使ってログを出力しています。なので、もし Log4j を使っている場合はそのままではログが出力されなくなるので、slf4j でのログ出力を log4j 経由にするブリッジライブラリを設定します。
詳しくはブログの通り。
dependencies だけでなく、logger設定の org.dbflute パッケージへの変更を忘れずに。
Slf4jを使っててもパッケージを修正
最初から Slf4j (LogBack) を使っていたとしても、やはりlogger設定の org.dbflute パッケージへの変更を忘れずに。
まず、互換モードを全開にする
littleAdjustmentの互換オプション
大きく二種類あります。
- compile compatible
- コンパイルレベルで移行できてるか検知できる
- runtime compatible
- アプリを実行しないと大丈夫かどうかわからないもの
厳密には、"compile compatible" の中には、sql2entity や outside-sql-test タスクを実行すれば検知できるものも含まれています。要は、アプリの実行前に検知できるものと考えるとよいでしょう。
以下の "赤い部分" を littleAdjustmentMap.dfprop にコピーして、manage の renewal をします。すべての互換オプションをONにしています。
e.g. 互換モード全開 @littleAdjustmentMap.dfprop
...
# o relationalNullObjectMap: (NotRequired - Default map:{})
#
# *The line that starts with '#' means comment-out.
#
map:{
# _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
# Migration to Java8 DBFlute
# https://jflute.hatenadiary.jp/entry/20140530/java8
# _/_/_/_/_/_/_/_/_/
# compile compatible (true if compatible)
; isCompatibleSelectByPKOldStyle = true
; isCompatibleSelectByPKPlainReturn = true
; isCompatibleSelectByPKWithDeletedCheck = true
; isMakeConditionQueryExistsReferrerToOne = true
; isMakeConditionQueryInScopeRelationToOne = true
; isMakeConditionQueryPlainListManualOrder = true
; isMakeDirectConditionBeanSetup = true
; isMakeBatchUpdateSpecifyColumn = true
; isAvailableSelectEntityPlainReturn = true
; isAvailableRelationPlainEntity = true
; isMakeConditionQueryPrefixSearch = true
; isMakeConditionQueryDateFromTo = true
; isMakeDirectConditionOptionSetup = true
; isMakeDirectConditionManualOrder = true
; isMakeClassificationNativeTypeSetter = true
; isCompatibleNewMyEntityConditionBean = true
; isCompatibleDeleteNonstrictIgnoreDeleted = true
; isCompatibleLoadReferrerOldOption = true
; isCompatibleConditionBeanAcceptPKOldStyle = true
; isCompatibleConditionBeanOldNamingCheckInvalid = true
; isCompatibleConditionBeanOldNamingOption = true
; isCompatibleBizOneToOneImplicitReverseFkAllowed = true
; isCompatibleReferrerCBMethodIdentityNameListSuffix = true
; isCompatibleOutsideSqlFacadeChainOldStyle = true
; isCompatibleOutsideSqlSqlCommentCheckDefault = true
; isCompatibleSelectScalarOldName = true
# compile compatible (false if compatible)
; isMakeCallbackConditionBeanSetup = false
; isEntityDerivedMappable = false
; isMakeCallbackConditionOptionSetup = false
; isMakeCallbackConditionManualOrder = false
; isAvailableJava8TimeLocalDateEntity = false
# runtime compatible (true if compatible)
; isNullOrEmptyQueryAllowed = true
; isOverridingQueryAllowed = true
; isNonSpecifiedColumnAccessAllowed = true
; isCompatibleConditionBeanFromToOneSideAllowed = true
; isCompatibleOrScopeQueryPurposeNoCheck = true
; isSuppressDefaultCheckClassificationCode = true
# runtime compatible (false if compatible)
; isThatsBadTimingDetect = false
# /- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# o isAvailableAddingSchemaToTableSqlName: (NotRequired - Default false)
...
}
互換モード全開で renewal
manage の renewal をします。
importの編成を一気に
自動生成クラス、アプリのクラス、もろもろすべてのクラスに対して、importの編成 (Eclipseにて) を実行します。org.seasar.dbfluteの参照が、org.dbfluteに "どかどか" 変わっていくかと思います。
クラス名だけでユニークに解決できないクラスは、個別個別にソースを開いて、ctrl+shift+O をやらないといけないかもしれません。 どのクラスが解決できていないのかはこの時点で判断できないので、次のステップへ進んで一緒にやっていきます。
互換のないコンパイルエラーを直す
importの編成をやってもコンパイルエラーになっているものは、互換性のない機能変更によるもの、もしくは、importが解決できなかったものです。
grepの一括置換でOKなやつ
Eclipseなら ctrl+H で一括置換してもよいものです。
- ManualOrderBean を
- ManualOrderOption に
ただし、アプリで全然違う機能の同名クラスを自作している場合はダメです(ありえないと思いますが)。
地道に直す
地道に直すものです。
- DerivedReferrerOption
- DerivedReferrer のオプション指定は Lambda に
- ColumnConversionOption
- ColumnQuery のオプション指定は Lambda に
- ScalarSelectOption
- ScalarSelect のオプション指定は Lambda に
- ManualOrderOption
- ManualOrderOption の when の FromTo 指定は Lambda に
- PagingResultBean
- pageRange()などの設定の仕方が Lambda に
- DateFromTo
- DateFromTo自体がなくなったので FromTo で代用
- selectScalar()
- 戻り値が Optional に
- 外だしSQLの呼び出し
- フラットな呼び出しや traditionalStyle() など
"外だしSQLの呼び出し" に関しては、こちらのをページを。
実は、これは全部ではありません。その理由は二つ。
- 通常、ユーザーが利用しないものは省略している
- というか、そういった細かいものは、さすがに忘れてしまいました...ごめんなさい
アプリがDBFluteの内部的なクラスをどれだけさわっているかによります。 もし、どういう修正をすればよいのか判断の付かないコンパイルエラーがあったら、そこは遠慮なく jflute に聞いてください。
いったんテスト
コンパイルエラーが取れたら、アプリのテストをしてみましょう。
すんなり動けば、とりあえず 1.1 への移行は最低限できたことになります。 時間がなければ、ここで終わってもOKでしょう。
dfpropの移行
dfpropのコメントなど、1.1.xから変わっているものもあるので、間違い防止のためにも修正しておくとよいでしょう。
basicInfoのコメント
まずは、basicInfoMap.dfprop のヘッダーコメント、まるごとコピーして上書きしてしまいましょう。
e.g. basicInfoMap.dfprop のヘッダーコメント @basicInfoMap.dfprop
# /---------------------------------------------------------------------------
# basicInfoMap: (Required)
#
# The basic information for the tasks of DBFlute.
# You should specify before your first generating.
#
# Core Properties:
# o database: (Required)
# o targetLanguage: (Required)
# o targetContainer: (Required)
# o packageBase: (Required)
#
# Adjustment Properties:
# o generateOutputDirectory: (NotRequired - Default Java:'../src/main/java' CSharp:'../source')
# o resourceOutputDirectory: (NotRequired - Default '../resources')
# o isTableNameCamelCase: (NotRequired - Default false)
# o isColumnNameCamelCase: (NotRequired - Default false)
# o projectPrefix: (NotRequired - Default '')
# o classAuthor: (NotRequired - Default 'DBFlute(AutoGenerator)')
# o sourceFileEncoding: (NotRequired - Default 'UTF-8')
# o sourceCodeLineSeparator: (NotRequired - Default no setting)
# o applicationBehaviorMap: (NotRequired - Default map:{})
# o generationGapileMap: (NotRequired - Default map:{})
# o dbfluteSystemFinalTimeZone: (NotRequired - Default null)
#
# *The line that starts with '#' means comment-out.
#
classificationDefinitionのコメント
まずは、classificationDefinitionMap.dfprop のヘッダーコメント、まるごとコピーして上書きしてしまいましょう。
e.g. classificationDefinitionMap.dfprop のヘッダーコメント @classificationDefinitionMap.dfprop
# /---------------------------------------------------------------------------
# classificationDefinitionMap: (NotRequired - Default map:{})
#
# The definition of classification.
#
# Specification:
# map: {
# [classification-name] = list:{
# ; map:{
# ; topComment=[comment]; codeType=[String(default) or Number or Boolean]}
# ; undefinedHandlingType=[EXCEPTION or LOGGING(default) or ALLOWED]
# ; isUseDocumentOnly=[true or false(default)]
# ; isSuppressAutoDeploy=[true or false(default)]
# ; groupingMap = map:{
# ; [group-name] = map:{
# ; groupComment=[comment]
# ; elementList=list:{[the list of classification element's name]}
# }
# }
# }
# # classification elements for implicit classification
# ; map:{
# ; code=[code]; name=[name]; alias=[alias]; comment=[comment]
# ; sisterCode=[code or code-list]; subItemMap=map:{[free-map]}
# }
# # settings for table classification
# ; map:{
# ; table=[table-name]
# ; code=[column-name for code]; name=[column-name for name]
# ; alias=[column-name for alias]; comment=[column-name for comment]}
# ; where=[condition for select]; orderBy=[column-name for ordering]
# ; exceptCodeList=[the list of except code]
# }
# }
# }
#
# *The line that starts with '#' means comment-out.
#
littleAdjustmentのコメント
まずは、littleAdjustmentMap.dfprop のヘッダーコメント、まるごとコピーして上書きしてしまいましょう。
e.g. littleAdjustmentMap.dfprop のヘッダーコメント @littleAdjustmentMap.dfprop
# /---------------------------------------------------------------------------
# littleAdjustmentMap: (NotRequired - Default map:{})
#
# The various settings about a little adjustment.
#
# o isAvailableAddingSchemaToTableSqlName: (NotRequired - Default false)
# o isAvailableAddingCatalogToTableSqlName: (NotRequired - Default false)
# o isAvailableDatabaseDependency: (NotRequired - Default false)
# o isAvailableDatabaseNativeJDBC: (NotRequired - Default false)
# o isAvailableNonPrimaryKeyWritable: (NotRequired - Default false)
# o classificationUndefinedHandlingType: (NotRequired - Default LOGGING)
# o isEntityConvertEmptyStringToNull: (NotRequired - Default false)
# o isMakeConditionQueryEqualEmptyString: (NotRequired - Default false)
# o isTableDispNameUpperCase: (NotRequired - Default false)
# o isTableSqlNameUpperCase: (NotRequired - Default false)
# o isColumnSqlNameUpperCase: (NotRequired - Default false)
# o isMakeDeprecated: (NotRequired - Default false)
# o isMakeRecentlyDeprecated: (NotRequired - Default true)
# o extendedDBFluteInitializerClass: (NotRequired - Default null)
# o extendedImplementedInvokerAssistantClass: (NotRequired - Default null)
# o extendedImplementedCommonColumnAutoSetupperClass: (NotRequired - Default null)
# o shortCharHandlingMode: (NotRequired - Default NONE)
# o quoteTableNameList: (NotRequired - Default list:{})
# o quoteColumnNameList: (NotRequired - Default list:{})
# o columnNullObjectMap: (NotRequired - Default map:{})
# o relationalNullObjectMap: (NotRequired - Default map:{})
# o cursorSelectFetchSize: (NotRequired - Default null)
# o refreshMap: (NotRequired - Default map:{})
# o optimisticLockMap: (NotRequired - Default map:{})
#
# *The line that starts with '#' means comment-out.
#
outsideSqlDefinitionのコメント
outsideSqlDefinitionMap.dfprop は、isRequiredSqlTitle と isRequiredSqlDescription のデフォルト値が true に変わっているので、ヘッダーコメントと、プロパティ定義部分のコメントを修正しましょう。
追加されたプロパティは?
この状態では、追加されているプロパティの雛形はありませんが、使わないのであればそのままでもよいでしょう。 とりあえずヘッダーにさえあれば、それに気付いて設定しようとしたときに、mydbfluteのDBFluteクライアントの雛形からコピーして持ってくればOKです。
ちなみに、追加されているのは、dbfluteSystemFinalTimeZone や classificationUndefinedHandlingType, columnNullObjectMap などです。
がらっと変わった未定義区分値のプロパティ
未定義区分値のプロパティががらっと変わっています。 無理することはありませんが、もしよければ移行という感じで。
dfpropのファイル名の変更
ファイル名が修正された dfprop があります。古い名前でも動作しますが、移行しておくと良いでしょう。
もう少し攻める
少ない時間でちょっとやれそうだったらやってみるとよいものです。
現実的なコンパイルレベルでの検知
まずは、true から false にして自動生成し直してみて、コンパイルエラーがでるかどうか? もし、エラーの数が少なく、すぐに直せそうなら直してしまいましょう。
一個ずつ false にして自動生成して直して、また次を一個 false にして...を繰り返すとよいでしょう。
- isCompatibleSelectByPKOldStyle (to false)
- selectByPKValue() から selectByPK() に
- 利用箇所は少ないと想定され、一括置換でも修正できそうなのでやれたらやってみるとよい
- isMakeConditionQueryExistsReferrerToOne (to false)
- one-to-one への ExistsReferrer を生成しないように
- 相手側PKによる IsNotNull もしくは IsNull で代用
- isMakeConditionQueryInScopeRelationToOne (to false)
- many-to-one への InScopeRelation を生成しないように
- 普通のQueryRelationによる条件付与で代用
- isMakeConditionQueryPlainListManualOrder (to false)
- ManualOrder の List 受け取りメソッドを生成しないように
- ManualOrderOption の acceptOrderValueList() で代用
- isMakeBatchUpdateSpecifyColumn (to false)
- batchUpdate()のSpecifyするオーバーロードを生成しないように
- そもそもbatchUpdate()がSetter呼び出し方式なので、プログラムを見て同じupdateになるならOK
- もし、Setter呼び出しじゃないようにする互換オプションを使っていたら、ちょっとやめておきましょう
- isMakeConditionQueryPrefixSearch (to false)
- PrefixSearchを生成しないように、LikeSearchOption の likePrefix() で代用
- isMakeConditionQueryDateFromTo (to false)
- DateFromToを生成しないように、FromToOption の compareAsDate() で代用
- isMakeClassificationNativeTypeSetter (to false)
- 区分値カラムのネイティヴ型引数のメソッドを作らないように
- もともと isForced... を使っている場合は false にして問題ないはず
- isCompatibleNewMyEntityConditionBean (to false)
- Behavior の newMyCondition() が newConditionBean() が変わった (Entityも)
- isCompatibleDeleteNonstrictIgnoreDeleted (to false)
- Behavior の deleteNonstrictIgnoreDeleted() を生成しないように
- try catch で代用
- isCompatibleLoadReferrerOldOption (to false)
- LoadReferrer の LoadReferrerOption を使うオーバーロードメソッドを生成しないように
- ネストした LoadReferrer は、loader方式で代用
- isCompatibleConditionBeanAcceptPKOldStyle (to false)
- cb.acceptPrimaryKey() が cb.acceptPK() に変わった
- isCompatibleConditionBeanOldNamingCheckInvalid (to false)
- cb.checkInvalidQuery() が cb.checkNullOrEmptyQuery() に変わった
- isCompatibleConditionBeanOldNamingOption (to false)
- cb の細かいオプションたちのメソッド名が cb.enableXxx() or cb.disableXxx() に変わった
- isCompatibleBizOneToOneImplicitReverseFkAllowed (to false)
- 暗黙の逆参照FKを生成しないように、もし必要なら普通に additionalForeignKey で定義
- isCompatibleReferrerCBMethodIdentityNameListSuffix (to false)
- 例えば、existsPurchaseList() が existsPurchase() に、だいぶスッキリします
- isCompatibleOutsideSqlFacadeChainOldStyle (to false)
- 例えば、outsideSql().cursorHandling().selectCursor() が outsideSql().selectCursor() に
- TypedParameterBeanじゃないものは、outsideSql().traditionalStyle().select... に
- 外だしSQLの数次第で、移行が現実的かどうか決まるかと
- isCompatibleOutsideSqlSqlCommentCheckDefault (to false)
- 外だしSQLのコメントを必須に(OutsideSqlTestなどでチェック)、こちらも外だしSQLの数次第
- isCompatibleSelectScalarOldName (to false)
- Behaviorのメソッド、scalarSelect() ではなく selectScalar()に
逆に false から true にするやつは、こちらです。
- isEntityDerivedMappable (to true)
- DerivedReferrer で derived() が使えるようにする
- 別に互換を損なうことはないので、問答無用で true にしてしまってもいいかと
- アプリのポリシーとして、いまさらこっちは使わないとかであればそのままで
現実的な実行レベルでの検知
実行しないとわからないものはそもそも現実的ではないのですが、それでもまあいけるんじゃないかな? っていうのと、あとそんな実装があったらそもそも困るみたいなものです。 開発中なのか、既に運用中なのかでだいぶニュアンスが変わってくると思います。
true から false にして自動生成し直してみて、テストしてみましょう。 あとで、一括テストするのであれば、とりあえず設定しておくだけでもいいかもです。
- isNonSpecifiedColumnAccessAllowed (to false)
- SpecifyColumnしたのに、Specifyされていないカラムをgetするのを許す
- そもそもバグかもしれないので false に。どうしても必要なら、cb.disableNonSpec...()
- isCompatibleOrScopeQueryPurposeNoCheck (to false)
- OrScopeQueryのコールバックの中でSetupSelectやOrderByしているのを許す
- 許したくないので false に
- isSuppressDefaultCheckClassificationCode (to false)
- 区分値に定義されているもの以外の区分値をsetしたりgetしたりを許す
- 許したくないので false に (隠れ区分値がなければ)
そして、逆に false から true にするやつです。
- isThatsBadTimingDetect (to true)
- 例えば、ExistsReferrerのコールバックの中で外側の cb を呼び出してるとか
- Lambdaになるとこの間違いによるバグが増えそうなので、ぜひ true にしたい
チャレンジな世界へ...はちょい待って
Optional や Lambda への移行は大変
残ったプロパティは、Optional や Lambda の導入などが絡んでくるなど、かなりチャレンジです。
開発初期段階であれば、いっせーのでできるかもですが、開発終盤では厳しいでしょう。 運用中のものであれば移行祭りが必要です。なので、ここまでできていれば十分ではあるでしょう。
ちょっとずつ移行
徐々に移行していきたいのであれば、Lambdaのコールバックのメソッドは生成しておいて、 新しいプログラムはオーバーロードのLambda方式のメソッドを使って、既存のコードは修正が入ったときにつどつどリファクタリングするというやり方もできます。
ただ、補完すると selectList() や selectEntity() が全部オーバーロードで二つに分身するので、ちょっとうっとおしいかもしれません。 やるなら本当に徐々に直していって、最終的には全部 Lambda に直す意気込みが必要でしょう。
ConditionBean の new 方式 (isMakeDirectConditionBeanSetup) をテーブル単位で互換させるオプションが用意されています。 これを使って少しずつ互換テーブルを消していくとよいでしょう。
e.g. テーブル単位でCBを互換モードにする @littleAdjustmentMap.dfprop
; isMakeDirectConditionBeanSetup = true
; makeDirectConditionBeanSetupTableList = list:{
; prefix:MEMBER ; PURCHASE ; prefix:Vendor ; prefix:White
}
それでもコンパイルエラー検知ではあるので...
Optionalの方は、こういった器用なことができないので、移行するときや "えいやっ" とみんなで一斉に直すしかないでしょう。ただ、Lambdaもそうですが、コンパイルエラーの検知のできるものではありますので、"すごい頑張ればできる" という感じでしょうか。(でも、リフレクション詰め替えを使っていたらアウト)
Optionalにすると実装の安全性は格段に上がります。Lambdaは別に...明確なメリットが得られるかというとそうではないかもしれませんが、 実は Optional は多少苦労しても一番移行したいと思えるものかもしれません。
java.util.Date を LocalDate への移行は、さらにしんどいと思うので無理することはないでしょう。