2024.12.05
【弊社紹介】社内勉強会開催!2024上期総集編
2018.04.20
プログラミングDomaの好きなとこ3選
こんにちは、MTです。
今回は、JavaのDBアクセスライブラリ「Doma」の好きなところを3つ紹介させてください。
※実際にDomaを使用するための設定等は割愛させていただきます。
Doma = Domain Oriented Database MApping Frameworkの略。
Java製のDBアクセスライブラリになります。(ORマッパーというよりは、Resultマッパー)
MyBatisのようにDaoとSQLファイルを用意して、クエリの発行結果をエンティティにマッピングするタイプのライブラリです。(なのでResultマッパー)
現在はバージョン2までで、現在バージョン3の製作が進んでいるところです。
2waySQLが何なのかを説明しておくと、「素のままでも、アプリケーションからも使用することもできるSQL」を指します。
student
デーブルから id
を指定してレコードを取得する場合、アプリケーション側で作成するSQLは以下のようになります。
SELECT
/*%expand*/*
FROM
student
WHERE
id = /* id */0
;
※ %expand
はDomaの機能で、エンティティの全項目を展開してくれるものです。
素のSQLとしては /* ~ */
で囲われた部分はコメント扱いになるので、DBクライアント等からそのままクエリを発行することが可能です。
また、アプリケーション(Doma)から発行されるクエリは
SELECT
id, first_name, last_name, ... // エンティティの項目が展開される
FROM
student
WHERE
id = ? // プレースホルダに置き換わる
;
となります。
このように、素のSQLとしても、アプリケーション用としても使用できるため開発効率は良いはずです。
※勿論、実際にアプリケーションから発行されるクエリの妥当性等はテストする必要があります。
少し話は変わりますが、こんな経験はありませんか?
// 生徒エンティティ
@Entity
public class Student {
private long id;
private String firstName;
private String lastName;
private int age;
// getter, setter は省略
}
// 生徒の情報を登録する処理
InputForm form = // 画面から入力された情報
Student student = new Student();
student.setId(form.getStudentId());
student.setFirstName(form.getStudentFirstName());
student.setLastName(form.getStudetnFirstName()); // lastName に firstName 入れてる。。。
student.setAge(form.getStudentAge());
studentDao.insert(student);
上記のプログラムでは firstName
と lastName
の型が同じ String
なので、コンパイルエラーにはなりません。
人間からすれば違う項目であることは分かりますが、それは人間の話。
コンピュータ(コンパイラ)からすれば、どちらも String
なので正しいとしか判断してくれません。
このように、異なる項目に設定してしまい、実行後のテーブルを確認して初めてミスに気付くパターン、経験ありませんか?
Domaは ValueObject が使用できるので、上記のようなミスを防ぐことができます。
上記の Student
エンティティを、ValueObjectの項目に置き換えると以下のようになります。
@Entity
public class Student {
private ID id;
private FirstName firstName;
private LastName lastName;
private Age age;
// getter, setter は省略
}
このように ValueObject を使用することで、間違った項目に値を設定した場合でもコンパイルエラーになります。
InputForm form = // 画面から入力された情報(InputFormもValueObjectの項目を保持していると想定)
Student student = new Student();
student.setId(form.getStudentId());
student.setFirstName(form.getStudentFirstName());
student.setLastName(form.getStudetnFirstName());
student.setAge(form.getStudentAge());
studentDao.insert(student);
型 Student のメソッド setLastName(LastName) は引数 (FirstName) に適用できません
項目の数だけ ValueObject を作成する必要がありますが、作成するコスト以上のメリット(型による恩恵)があると思っています。
Domaには「Collect検索」という機能が備わっています。
「Collect検索」とは
検索結果を
java.util.Collector
で処理したい場合は、コレクト検索を利用できます。
です。
Domaでは複数件検索の結果は、メソッドの戻り値を List<T>
にします。
@Dao
public interface StudentDao {
@Select
List<Student> findAll();
}
こんな感じ。
この場合 StudentDao#findAll
は List<Student>
しか返せませんが、
年齢でグルーピングした Map<Age, List<Student>>
で受け取りたい場合はDaoの呼び出し元で Map
への変換を行う必要があります。
Map<Age, List<Student>> studentMap
= studentDao.findAll().stream().collect(Collectors.groupingBy(Student::getAge));
単に Map
で扱いたいだけなのに、わざわざ変換処理を噛ますのは面倒ですね。
そこで「Collect検索」機能です。
「Collect検索」機能は、Daoメソッドの引数に Collector
を渡すことで任意の型で結果を受け取ることができるようになります。
まずDaoメソッド findAll
に Collector
を受け取るようにします。
@Dao
public interface StudentDao {
@Select(strategy = SelectType.COLLECT)
<R> R findAll(final Collector<Student, ?, R> collector);
}
@Select(strategy = SelectType.COLLECT)
の部分は「Collect検索」を使用する宣言だと思ってください。
findAll
の戻り値は任意型にしたいので R
、引数には Collector<Student, ?, R>
を受け取るようにします。
これで「Collect検索」を利用することができます。
Map<Age, List<Student>> studentMap = studentDao.findAll(Collectors.groupingBy(Student::getAge));
大分スッキリしました。
また、最初と同じく List<Student>
で受け取りたい場合は List
に変換する Collector
を渡せばいいので
List<Student> studentList = studentDao.findAll(Collectors.toList());
こうなります。
Collector
を作ってしまえば、様々な型で受け取ることができるため、柔軟性が高くなります。
「Collecto検索」素敵ですね。
他にも好きな点はあるのですが、今回は3点紹介させていただきました。
Domaは静的型付け言語のメリットを生かすことができるライブラリですので、是非使っていただきたいです。
【記事への感想募集中!】
記事への感想・ご意見がありましたら、ぜひフォームからご投稿ください!【テクノデジタルではエンジニア/デザイナーを積極採用中です!】
下記項目に1つでも当てはまる方は是非、詳細ページへ!Qangaroo(カンガルー)
【テクノデジタルのインフラサービス】
当社では、多数のサービスの開発実績を活かし、
アプリケーションのパフォーマンスを最大限に引き出すインフラ設計・構築を行います。
AWSなどへのクラウド移行、既存インフラの監視・運用保守も承りますので、ぜひご相談ください。
詳細は下記ページをご覧ください。
最近の記事
タグ検索