Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Doma 2 素案

Doma 2 素案

この文書は、今後 Doma 2 を開発していくにあたり、新規追加する機能について作者の考えを述べたものです。

前提

  • Doma は Annotation Processing Tool (apt) を利用した Java の Dao フレームワークである
  • Doma 1 を Java 6 で開発してきたが、Java 8 への対応に伴い Doma 2 へメジャーバージョンアップする
  • サポートする Java のバージョンは 8 以上とする
  • Java 8 ならではの機能を追加するとともに、既存機能の拡張も行う
  • Doma 1 との互換性はできるだけ保つが 100 %は目指さない (互換性がない箇所については移行方法を示す)
  • Doma 1 の開発から掲げてきたコンセプトは変えない
  1. 暗黙的な規約よりも明示的な設定を重視する
  2. 他のライブラリに依存しない
  3. できる限り実行時ではなくコンパイル時にエラーを検出する
  4. できる限りキャッシュしない
  5. わかりやすいエラーメッセージを出力する)

検討している機能

java.util.Optional への対応

java.util.Optional を以下の箇所で利用可能とします。

  • エンティティのプロパティ
  • Dao メソッドのパラメータや戻り値
@Dao(config = AppConfig.class)
public interface EmpDao {
  @Select
  Optional<Emp> selectByName(String name);
}

@Delegate の廃止と default メソッドのサポート

DataSourceを直接使う処理などを行う場合、Doma 1 では @Delegate アノテーションを指定することで、別クラスに処理を委譲していました。 Java 8 では、default キーワードを使うことでインタフェースに実装を持てるようになったのでこちらを採用します。

また、Doma 2 では Config を取得する手段を新しく提供します。

@Dao(config = AppConfig.class)
public interface EmpDao {
  default Optional<Emp> selectByName(Optional<String> name) {
     // Dao に定義された Config の インスタンスを取得
     Config config = Config.get(this);
     DataSource dataSource = config.getDataSource();
     ...
  }
}

org.seasar.doma.jdbc.IterationCallback のヘルパーの追加

IterationCallback に関してラムダ式を使った簡潔な記述をサポートします。

@Dao(config = AppConfig.class)
public interface EmpDao {
  @Select(iterate = true)
  Salary iterate(IterationCallback<Salary, Emp> callback);
}

たとえば、上記の定義に対し次のような記述ができるようなヘルパーメソッドを提供します。

Salary sum = dao.iterate(Iter.reduce(Salary.ZERO, (acc, emp) -> acc.add(emp.salary)));

拡張ポイントに Dao メソッドの java.lang.reflect.Method インスタンスを渡す

org.seasar.doma.jdbc.JdbcLoggerorg.seasar.doma.jdbc.SqlFileRepository の実装は任意にカスタマイズできますが、これらの実装クラスに処理中の Dao メソッドに対応する java.lang.reflect.Method インスタンスをパラメータとして渡すことにします。 これにより、たとえばメソッドに注釈されたアノテーションにより条件分岐するといったことが可能になります。

Dao メソッドの実装のカスタマイズをサポート

Doma により自動生成される Dao メソッドの実装コードは、主に org.seasar.doma.internal.jdbc.query.Queryorg.seasar.doma.internal.jdbc.command.Command と呼ばれる2つの internal なコンポーネントを利用しています。

Doma 2 ではこれらのコンポーネントを public な API とし、Config で実装クラスを差し替えられるようにします。これにより、SQL の生成処理や JDBC の API へのアクセス方法などをカスタマイズ可能になります。

JSR 310 Date and Time API への対応

具体的な対応は現時点では考えていません。理由は以下の通りです。

  • JDBC ドライバごとに対応がまちまちになるのでは?という懸念がある
  • 日付に関する処理はドメインクラスでラッピングして使ったほうが扱いやすいことを考えると JDBC で JSR 310 を直接扱う必要性が低いと思われる
  • java.sql.Date/Time/Timestamp や java.util.Date にマッピングしたドメインクラスの中で適宜変換をかければよい

java.util.stream.Stream による遅延ローディング

次のように記述して実現する案をいただきました。

@Dao(config = AppConfig.class)
public interface EmpDao {
  @Select(lazy = true)
  Stream<Emp> selectAll();
}

しかし、次の懸念点があるため現時点では対応に消極的です。

  • 機能的には @Select(iterate = true) と重複する
  • Stream は AutoCloseable であるが、try で囲まないと close 忘れが発生する
  • トランザクション境界の外でアクセスされる可能性がある
  • 遅延ローディングは行わないという Doma のポリシーに反する

RDBMSで予約されているキーワードの利用をサポート

たとえば、Order というテーブルを作った場合、SQL の中ではダブルクォートで囲むなどの処理が必要です。

delete from "Order" where id = 1

このような SQL を生成するためのオプションを用意します。 @Table などのアノテーションに新たな要素を追加してダブルクォートで囲むことを示せるようにします。

@Entity
@Table(enclose = true)
public class Order {
  ...
}

注釈処理のオプションの名前を整理

Doma 1 では 9 つのオプションを提供していますが、Doma のオプションとわかるような名前空間がないのでわかりにくくなっています。整理します。

@gakuzzzz

IterationCallback<ENTITY, RESULT> を廃止して代わりに Function <Stream, RESULT> を受け取れるようにするのは如何でしょうか?
そうすれば Stream の豊富な機能も使えますし、Collectorなど色々使いまわせるようになります。

java.util.stream.Stream で懸念されているような

  • Stream は AutoCloseable であるが、try で囲まないと close 忘れが発生する
  • トランザクション境界の外でアクセスされる可能性がある

といった問題もある程度解消できるのかな、と思います。

// もちろん Function<Stream, Stream> みたいなのも渡せちゃうので完全に防げはしないですが……。

@nakamura-to
Owner

@gakuzzzz なるほど、とてもいいアイデアだと思います!
IterationContext を渡す必要がなくなりますし、IterationCallback のヘルパーも不要そうですね。

// もちろん Function<Stream, Stream> みたいなのも渡せちゃうので完全に防げはしないですが……。

ほとんどのケースでは問題にならないので大丈夫だと思います!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment