[Seasar-user:20277] Re: [DBFlute]OracleでDATE型カラムのインデックスが利用されない

kubo [E-MAIL ADDRESS DELETED]
2010年 10月 22日 (金) 15:42:39 JST


久保(jflute)です。

しゃってんさん、ありがとうございます。
もろもろ特にそちらとこちらで現象に違いはなさそうですね。

とりあえず、DBFluteにて正式に対応しました。
デフォルトは今まで通り、Timestamp にてバインド。
littleAdjustmentMap.dfprop のオプションで
oracle.sql.DATE を使った最適化を利用することができます。
詳しくは、ドキュメントをご覧下さい。

// DBMSごとの取扱い - Oracleの日付型の最適化 | DBFlute
http://dbflute.sandbox.seasar.org/ja/manual/reference/dbway/oracle/index.html#oracledate

モジュール:DBFlute-0.9.7.5-SNAPSHOT
ランタイム:DBFlute-0.9.7.5-04-SNAPSHOT
にて反映されています。

そもそもアップグレードできない状況であれば、
DBFluteのソースを参考に、アプリ独自の ValueType を作って、
DBFluteConfig経由でその ValueType を登録して下さい。
アプリケーションの初期化時に DBFluteConfig の
registerBasicValueType() で登録すればOKです。

以下を参考:
o org.seasar.dbflute.s2dao.valuetype.plugin.OracleDateType
o ...allcommon.DBFluteConfig のVelocityテンプレート

とにかくアプリに oracle.sql.DATE を new してもらう、
という感じですね。それ遺骸は、ランタイム内で全て解決。

2010/10/22 しゃってん <[E-MAIL ADDRESS DELETED]>:
> しゃってんです。
>
>> 普通に更新で entity.setXxx(null) して更新してみて下さい。
> こちらの環境で実行した場合もNullPointerException が発生しました。
>
> TnValueTypes.registerBasicValueType(keyType, new UtilDateAsTimestampType() {
>       @Override
>       public void bindValue(PreparedStatement ps, int index, Object
> value) throws SQLException {
>           if (value==null) {
>               ps.setNull(index, java.sql.Types.TIMESTAMP);
>           } else {
>               ps.setObject(index, new
> oracle.sql.DATE(DfTypeUtil.toTimestamp((java.util.Date) value)));
>               //super.bindValue(ps, index, value);
>           }
>       }
>    }); // switch
>
> とすることでNullPointerException は回避はできますね。
>
>
> 2010年10月22日14:41 kubo <[E-MAIL ADDRESS DELETED]>:
>> 久保(jflute)です。
>>
>>> ただその前に、もう一つお願いしたいです。
>>> oracle.sql.DATEを有効にした状態で、バッチ更新を使って、
>>> 該当のカラムを更新してみて下さい。
>>> 実はこちらで試してて、oracle.sql.Datum クラス内で
>>> NullPointerException が発生してしまいました。
>> バッチ更新ってわけじゃなく、該当カラムを
>> null で更新しようとすると発生するようです。
>> 普通に更新で entity.setXxx(null) して更新してみて下さい。
>>
>> 2010/10/22 kubo <[E-MAIL ADDRESS DELETED]>:
>>> 久保(jflute)です。
>>>
>>> しゃってんさん、色々と検証ありがとうございます。
>>> 状況が随分はっきりしてきましたね。
>>> (小林さんのフォローに感謝感謝です)
>>>
>>>> littleAdjustmentMap.dfprop あたりでのオプション扱いとして、
>>>> 上記インナークラス部分を出力するような感じでもいいかもしれませんね。
>>>
>>> 勘がいいですねー。そういう感じのつもりです。
>>> ただその前に、もう一つお願いしたいです。
>>> oracle.sql.DATEを有効にした状態で、バッチ更新を使って、
>>> 該当のカラムを更新してみて下さい。
>>> 実はこちらで試してて、oracle.sql.Datum クラス内で
>>> NullPointerException が発生してしまいました。
>>> もしかしたら、検索条件のときだけ有効にするとか、
>>> バッチ更新では除外するとか、何かしら処理を入れる
>>> 必要があるのかもしれません。
>>>
>>> 2010/10/22 しゃってん <[E-MAIL ADDRESS DELETED]>:
>>>>> ((OraclePreparedStatement) ps).setDATE(index, new DATE(date));
>>>> #
>>>> # org.seasar.extension.dbcp.impl.ConnectionPoolImpl を利用している場合、
>>>> # Connection connection =
>>>> ((ConnectionWrapperImpl)dataSource.getConnection()).getPhysicalConnection();
>>>> # としないとClassCastExceptionとなるんですね。。。
>>>> #
>>>> で実行した場合、時刻まで考慮された検索結果が取得され
>>>> INDEX RANGE SCAN で検索が実行されました。
>>>>
>>>>
>>>> 同様に
>>>>> TnValueTypes.registerBasicValueType(keyType, new UtilDateAsTimestampType() {
>>>>>    @Override
>>>>>    public void bindValue(PreparedStatement ps, int index, Object
>>>>> value) throws SQLException {
>>>>>        ps.setObject(index, new
>>>>> DATE(DfTypeUtil.toTimestamp((java.util.Date) value)));
>>>>>        //super.bindValue(ps, index, value);
>>>>>    }
>>>>> }); // switch
>>>> #
>>>> # SQLの組み立て時にFromToOption を利用すると時刻の指定ができなかったので
>>>> # cb.query().setTargetDate_GreaterEqual(fromDate);
>>>> # cb.query().setTargetDate_LessThan(toDate);
>>>> # に変更しています。
>>>> #
>>>> の場合も時刻が考慮された検索結果が取得され、
>>>> INDEX RANGE SCAN で検索が実行されました。
>>>>
>>>> littleAdjustmentMap.dfprop あたりでのオプション扱いとして、
>>>> 上記インナークラス部分を出力するような感じでもいいかもしれませんね。
>>>> ※あとFromToOptionで無変換で返すクラスがあればいいかなと。
>>>>
>>>>
>>>> 2010年10月22日3:24 kubo <[E-MAIL ADDRESS DELETED]>:
>>>>> 久保(jflute)です。
>>>>>
>>>>> しゃってんさん、まだDBFluteとしてどうするか考え中ですが、
>>>>> DBFluteConfig にて、
>>>>>
>>>>> TnValueTypes.registerBasicValueType(keyType, valueType); // switch
>>>>>
>>>>>>>>>>
>>>>> TnValueTypes.registerBasicValueType(keyType, new UtilDateAsTimestampType() {
>>>>>    @Override
>>>>>    public void bindValue(PreparedStatement ps, int index, Object
>>>>> value) throws SQLException {
>>>>>        ps.setObject(index, new
>>>>> DATE(DfTypeUtil.toTimestamp((java.util.Date) value)));
>>>>>        //super.bindValue(ps, index, value);
>>>>>    }
>>>>> }); // switch
>>>>>
>>>>> に差し替えて試してみて下さい。
>>>>> (「DATE」は、oracle.sql.DATE です)
>>>>> 恐らく、java.sql.Date を指定したときと同じ実行結果に
>>>>> なるんじゃないかと思われます。
>>>>> こちらでも、30万件のテーブルを用意して比べてみると、
>>>>> 安定してパフォーマンスが改善されます。
>>>>> (30万件程度なのでわずかですが10msほど安定して差がでます)
>>>>>
>>>>> ※これをどう整理して提供するかがポイントですね...
>>>>>
>>>>> 2010/10/21 kubo <[E-MAIL ADDRESS DELETED]>:
>>>>>> 久保(jflute)です。
>>>>>>
>>>>>>>> O/Rマッパには厳しい話ですね。。。
>>>>>>>> いっそリフレクションでガツンってのは
>>>>>>>> やはり無理矢理過ぎるかなぁ。
>>>>>>>
>>>>>>> リフレクションでガツンとは?
>>>>>>
>>>>>> コンパイルレベルでDBMSのJDBCに依存したくないので、
>>>>>> リフレクションで oracle.sql.DATE を利用するように処理する
>>>>>> って感じですね。(もちろん、Oracleの場合に限り)
>>>>>>
>>>>>> 自動生成ありきのツールなので、oracle.sql.DATE を利用する
>>>>>> ValueType を自動生成するのが現実的かもと今考えています。
>>>>>> (OracleのDATE型の場合はそのValueTypeを使う)
>>>>>> アプリは基本的にOracleのJDBCに依存しても問題ないので。
>>>>>> (一応、オプションで)
>>>>>>
>>>>>> 2010/10/21 Koichi Kobayashi <[E-MAIL ADDRESS DELETED]>:
>>>>>>> 小林 (koichik) です.
>>>>>>>
>>>>>>> Date:    Thu, 21 Oct 2010 20:52:00 +0900
>>>>>>> From:    kubo <[E-MAIL ADDRESS DELETED]>
>>>>>>> To:      [E-MAIL ADDRESS DELETED]
>>>>>>> Subject: [Seasar-user:20268] Re: [DBFlute]OracleでDATE型カラムのインデックスが利用されない
>>>>>>>
>>>>>>>> なるほど、そもそもDBMSに依存しないJDBCの型の中で、
>>>>>>>> Oracle の DATE 型をぴったし当てはめられる型が存在しない
>>>>>>>> というところですかね。
>>>>>>>
>>>>>>> そうですね.
>>>>>>> JDBC および標準 SQL には日時 (日付と時刻) を
>>>>>>> 扱うデータ型が基本的に TIMESTAMP 1 種類
>>>>>>> (TZ 等は精度と同じ扱いとして) しかないのに対し,
>>>>>>> Oracle には DATE と TIMESTAMP の 2 種類があり,
>>>>>>> どちらも java.sql.Timestamp にマッピングするのが
>>>>>>> 問題の根本でしょうね.
>>>>>>> 歴史的なものなのでしょうがないですけど.
>>>>>>>
>>>>>>>> DATE型カラムのメタデータが:
>>>>>>>>   ojdbc5.jar = java.sql.Types.DATE
>>>>>>>>   ojdbc6.jar = java.sql.Types.TIMESTAMP
>>>>>>>> となっていたので TIMESTAMP の指定で問題ないかなと
>>>>>>>> 思ったのですが、(実際検索の動きとしては問題ないですが)
>>>>>>>> 内部的には型違いと。
>>>>>>>
>>>>>>> Oracle の DATE が java.sql.Types.DATE なのは
>>>>>>> 単純に間違ってますよね.
>>>>>>> Oracle の DATE は日付型ではなく日時型なので.
>>>>>>> ただし,Oracle の日時型には 2 種類ある...
>>>>>>>
>>>>>>>> > Oracle JDBC ドライバには oracle.sql.DATE という
>>>>>>>> > 型があるので、java.sql.Timestamp からこの型に
>>>>>>>> > 変換して、OraclePreparedStatement#setDATE() に
>>>>>>>> > 渡すなんてのが Oracle 的に好ましいやり方なのかも。
>>>>>>>>
>>>>>>>> O/Rマッパには厳しい話ですね。。。
>>>>>>>> いっそリフレクションでガツンってのは
>>>>>>>> やはり無理矢理過ぎるかなぁ。
>>>>>>>
>>>>>>> リフレクションでガツンとは?
>>>>>>>
>>>>>>>
>>>>>>> S2JDBC でも同じ問題は起こり得るので
>>>>>>> 何か考えた方がいいかなぁ.
>>>>>>> 幸い (?),S2JDBC では
>>>>>>>
>>>>>>> @Temporal(TemporalType.TIMESTAMP)
>>>>>>> public java.util.Date xxx;
>>>>>>>
>>>>>>> public java.sql.Timestamp xxx;
>>>>>>>
>>>>>>> という 2 種類の書き方ができるので,前者の場合は
>>>>>>> DATE,後者の場合は TIMESTAMP にマップして,
>>>>>>> 前者は OracleDialect で oracle.sql.DATE に
>>>>>>> 変換するとか.
>>>>>>> S2JDBC-Gen と辻褄合うのかなぁ...
>>>>>>>
>>>>>>>
>>>>>>> --
>>>>>>> <component name="koichik">
>>>>>>>    <property name="fullName">"Koichi Kobayashi"</property>
>>>>>>>    <property name="email">"[E-MAIL ADDRESS DELETED]"</property>
>>>>>>>    <property name="blog">"http://d.hatena.ne.jp/koichik"</property>
>>>>>>>    <property name="twitter">"http://twitter.com/koichik"</property>
>>>>>>> </component>
>>>>>>>
>>>>>>> _______________________________________________
>>>>>>> Seasar-user mailing list
>>>>>>> [E-MAIL ADDRESS DELETED]
>>>>>>> https://ml.seasar.org/mailman/listinfo/seasar-user
>>>>>>>
>>>>>>
>>>>> _______________________________________________
>>>>> Seasar-user mailing list
>>>>> [E-MAIL ADDRESS DELETED]
>>>>> https://ml.seasar.org/mailman/listinfo/seasar-user
>>>>>
>>>> _______________________________________________
>>>> Seasar-user mailing list
>>>> [E-MAIL ADDRESS DELETED]
>>>> https://ml.seasar.org/mailman/listinfo/seasar-user
>>>>
>>>
>> _______________________________________________
>> Seasar-user mailing list
>> [E-MAIL ADDRESS DELETED]
>> https://ml.seasar.org/mailman/listinfo/seasar-user
>>
> _______________________________________________
> Seasar-user mailing list
> [E-MAIL ADDRESS DELETED]
> https://ml.seasar.org/mailman/listinfo/seasar-user
>


Seasar-user メーリングリストの案内