Doma 1.27.0 をリリース

Doma 1.27.0 をリリースしました。

ダウンロードはこちらからどうぞ。

Mavenをご利用の方はこちらを参照ください。

以前のバージョンから移行するには移行ガイドを参照ください。

今回のバージョンには、重要な変更があります。移行ガイドの参照お願いします。

リリースノート

Improvement

  • [DOMA-265] - [Core] @Versionを注釈したプロパティの型にドメインの型を認めました。
  • [DOMA-266] - [Core] @GeneratedValueを注釈したプロパティの型にドメインの型を認めました。
  • [DOMA-269] - [Core] DomainクラスのgetValueメソッドの戻り値をジェネリック型で定義可能にしました。
  • [DOMA-270] - [Core] EntityListenerのコンテキストにListenerをトリガーしたDaoのメソッドやConfigの情報を含めました。
  • [DOMA-271] - [Core] @ExternalDomainの設定忘れに関するエラーメッセージをわかりやすくしました。
  • [DOMA-272] - [Core] EntityListenerの型引数が提供されていない場合のエラーメッセージをわかりやすくしました。

New Feature

  • [DOMA-273] - [Core] Domaが内部で使っているClass.forName(name)を差し替えられるようにしました。

機能紹介: お奨めの論理削除方法

DOMA-270 の修正でEntityListenerを強化しました。これに伴って論理削除が簡単にできるようになりました。

論理削除をフラグで行うケースで例を示したいと思います。

Dao

まずはDaoから見てみましょう。Daoはこのように書きます。

@Dao(config = AppConfig.class)
public interface EmpDao {
  @LogicalDelete
  @Update
  int delete(Emp emp);

  @Update
  int update(Emp emp);
}


呼び出し側は、削除をするつもりで処理をすればいいですから、論理削除を行うメソッド名はdeleteでいいでしょう。通常の更新はupdateメソッドで行うものとします。@UpdateはDomaアノテーションSQLのUPDATE文を発行することを示します。両方のメソッドに@Updateがついているので、Domaとしてはどちらのメソッドにも同じ処理を適用します。@LogicalDeleteはアプリで用意するアノテーションで、論理削除の更新と通常の更新(deleteメソッドが呼ばれたのかupdateメソッドが呼ばれたのか)を区別するためのマーカとして後で使います。@LogicalDeleteの定義は次のようなものです。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogicalDelete {
}
エンティティとリスナ

次はエンティティとリスナを見ましょう。こう書きます。

@Entity(listener = EmpListener.class)
public class Emp extends Common {
  ...
}
public class EmpListener extends CommonListener<Emp> {
  ...
}


ポイントは継承ですね。
EmpがCommonを継承しているのは、削除フラグをCommonに持たせているからです。
EmpListenerがCommonListenerを継承しているのは、多くのエンティティで論理削除処理を共通化するためです。

CommonとCommonListener

CommonとCommonListenerはこのようにします。

@Entity
public abstract class Common {
  public boolean flag;
}
public abstract class CommonListener<T extends Common> implements EntityListener<Common> {
  public void preUpdate(T entity, PreUpdateContext context) {
    if (context.getMethod().isAnnotationPresent(LogicalDelete.class)) {
      entity.flag = true;
    }
  }
  ...
}


共通エンティティのCommonは単に削除フラグを持つだけ。
共通リスナのCommonListenerは、更新処理が行われる直前(preUpdateメソッド)で削除フラグをセットします。preUpdateメソッドの2番目の引数のcontextがgetMethodメソッドを持っているところがポイントです。このgetMethodメソッドはDaoのメソッドオブジェクト(java.lang.reflect.Method)を返します。EmpDaoのdeleteメソッドもupdateメソッドもリスナをトリガーしますが、Daoのメソッドオブジェクトを見ればどちらが呼ばれたのか条件分岐できると言うことです。ここでは、@LogicalDeleteが注釈されているかどうかを見て条件分岐しています。

まとめ

EntityListenerのコンテキストに呼び出されたDaoメソッドのjava.lang.reflect.Methodオブジェクトが含まれるようになったので、EntityListenerで論理削除のフラグをセットするといった共通処理を行いやすくなりました。

preInsertやpreDeleteなどリスナの別のメソッドであっても、同じように2番目の引数のコンテキストからDaoのメソッドオブジェクトにアクセスできます。
また、コンテキストには、@Daoに指定するConfigオブジェクトも入っています。接続先のデータソースによって処理を振り分けるといったことも可能です。