Open3

Doma の DAO スタイルと DSL スタイルの使い分け

nakamura_tonakamura_to

Doma は2つのプログラミングスタイルを提供している。

  1. DAO スタイル: DAO のメソッドにアノテーションを付与し、SQL を自動生成させるか SQL を指定する方法
  2. DSL スタイル: Criteria API を使って Java コードで SQL を組み立てる方法

どう使い分けるかについては絶対の正解はないが、次のような基準で併用すると良いと思う。

  • 単純な追加、更新、削除は DAO スタイル
  • 単純な検索は DAO のデフォルトメソッドの中で DSL スタイル
  • 複雑な検索は DAO スタイル

コード例は以下の通り。

@Dao
public interface EmployeeDao {
  
  // ① 単純な追加、更新、削除などはアノテーションを付与して自動実行
  @Update
  int update(Employee employee);

  // ② パラメータが少数かつ単純な検索は Criteria API を利用
  default Employee selectById(int id) {
    var e = new Employee_();
    return QueryDsl.of(this).from(e).where(c -> c.eq(e.employeeId, id)).fetchOne();
  }

  // ③ パラメータが多数だが単純な検索は呼び出し元で Criteria API を利用
  default List<Employee> select(Function<QueryDsl, List<Employee>> block) {
    return block.apply(QueryDsl.of(this));
  }

  // ④ 複雑な検索は @Select アノテーションを付与して SQL ファイル か @Sql アノテーションを利用(ここでは SQL ファイルを利用)
  @Select
  List<Employee> selectWithComplexSql(int departmentId, Salary salary);
}

上記のコードを呼び出す例は次のようになる。

  @Test
  void test(Config config) {
    EmployeeDao dao = new EmployeeDaoImpl(config);

    // ②の実行
    var employee = dao.selectById(1);
    employee.setDepartmentId(2);
    // ①の実行
    dao.update(employee);

    // ③の実行
    var result =
        dao.select(
            dsl -> {
              var e = new Employee_();
              return dsl.from(e)
                  .where(
                      c -> {
                        c.eq(e.departmentId, 1);
                        c.ge(e.employeeNo, 1000);
                        c.between(e.salary, new Salary("1000"), new Salary("5000"));
                      })
                  .orderBy(c -> c.asc(e.employeeId))
                  .fetch();
            });
    System.out.println(result.size());

    // ④の実行
    var result2 = dao.selectWithComplexSql(1, new Salary("1000"));
    System.out.println(result2.size());
  }
nakamura_tonakamura_to

③の方法を採用するかどうかは議論の余地はある。

引数を DAO メソッドへ渡す手間を避けられるが、②の方法に統一した方が実行されるクエリが DAO インタフェースに閉じるのでメンテナンス性は高いかもしれない。

nakamura_tonakamura_to

②の方法は、④の方法に切り替えても呼び出し元のコードを一切影響を与えないという利点がある