2015-09-19 関西Kotlin勉強会
KotlinでDomaを使ってみよう!
Domaとは
- Java 8で動作するDBアクセスフレームワーク
- Pluggable Annotation Processing APIを利用してコンパイル時にコードの生成や検証を行う
- Pluggable Annotation Processing APIは他にはAndroidAnnotationsやLombok、Daggerなどで使用されている
- カラムを好きなクラスにマッピングできる
- 2-way SQL
- 依存ライブラリが無い
テーブルや検索結果をマッピングするやつ。
@Entity
public class Book {
@Id
public String isbn;
public String title;
public String author;
}
SQL投げて結果を返すやつ。
@Dao
public interface BookDao {
@Select
List<Book> select(String title, String author);
@Insert
int insert(Book book);
}
主にSELECT文で使う。
DAOのメソッドに対応したパスに置く。
例えば META-INF/app/dao/BookDao/select.sql
SELECT /*%expand*/*
FROM book
WHERE title = /* title */'x'
/*%if author != null */
AND author = /* author */'y'
/*%end*/
@Selectを付けたメソッドに対応するSQLファイルがないとコンパイルエラー- DAOクラスのメソッドに
@Selectや@Insertなどのアノテーションが付いていないとコンパイルエラー - SQLファイルの中身が空っぽだとコンパイルエラー
- メソッドの引数がSQLファイル内で使用されていないとコンパイルエラー
- SQLファイル内の
/*%if ...*/や/*%end*/が変な位置にあるとコンパイルエラー
エンティティのフィールドやDAOのメソッドの引数、戻り値にStringなどの基本型ではなくてユーザー定義のクラスを利用できる仕組み。
@Domain(valueType = String.class)
public class Isbn {
private final String value;
public Isbn(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
フィールドに使用できる。
@Entity
public class Book {
@Id
public Isbn isbn;
public Title title;
public Author author;
}
メソッドの引数や戻り値に使える。 (この例は引数だけ)
@Dao
public interface BookDao {
@Select
List<Book> select(Title title, Author author);
}
SQLファイルはドメインクラスを使用しない場合と何も変わらない。
SELECT /*%expand*/*
FROM book
WHERE title = /* title */'x'
/*%if author != null */
AND author = /* author */'y'
/*%end*/
//ドメインクラスがある
@Select
List<Book> select(Title title, Author author);
//ドメインクラスがない
@Select
List<Book> select(String title, String author);
ドメインクラスを使用していると
dao.select(author, title); //引数が逆
はコンパイルエラーになる。
(どちらもStringならコンパイルエラーにならない)
@Select(strategy = SelectType.STREAM)
<R> R select(Function<Stream<Book>, R> f);
@Select(strategy = SelectType.COLLECT)
<R> R select(Collector<Book, ?, R> collector);
@Select
Optional<Book> select(Isbn isbn);
Stream検索は次のように使う。
Optional<Book> book = dao.select(stream -> stream.findFirst());
List<Book> books = dao.select(Collectors.toList());
- ローカルトランザクション
transactionManager.required(() -> { //トランザクション内で実行される //例外が投げられたらロールバック }); - java.awt.Colorなど自由に変更できないクラスをドメインとして扱える(外部ドメイン)
- LocalDateTimeなどを扱える
- エンティティをイミュータブルにできる
@Entity(immutable = true) public class Book { public final Isbn isbn; public final Title title; public Book(Isbn isbn, Title title) { this.isbn = isbn; this.title = title; } }
- kapt: Annotation Processing for Kotlin
- KotlinでPluggable Annotation Processing APIが使える機能
TODO コード読んで概ねどんな感じで動作するのか説明したい
buildscript {
repositories.jcenter()
dependencies {
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:0.13.1513'
}
}
apply plugin: 'kotlin'
repositories.jcenter()
dependencies {
compile 'org.jetbrains.kotlin:kotlin-stdlib:0.13.1513'
compile 'org.seasar.doma:doma:2.4.1'
kapt 'org.seasar.doma:doma:2.4.1'
}
@Entity
public class Book {
@Id
val isbn: String? = null
val title: String? = null
val author: String? = null
}
(´-`).oO(あんまり変わらん)
@Domain(valueType = String::class)
public class Isbn(val value: String)
短くなった!
@Dao
public interface BookDao {
@Select
fun select(isbn: Isbn): Book
@Insert
fun insert(user: User): Int
}
(´-`).oO(あんまり変わらん)
-
少なくとも1つ注釈処理が動くJavaコード(エンティティクラスとかドメインクラス)が無いとkapt動いてくれない
-
注釈処理で取得できる引数名が
arg0,arg1... になるのでSQLファイルで参照するとき死ぬ。/** 理想 */ SELECT * FROM book WHERE isbn = /* isbn */'dummy'/** 現実 */ SELECT * FROM book WHERE isbn = /* arg0 */'dummy'あとイミュータブルなエンティティが使い物にならない。
//理想 @Entity(immutable = true) public class Book(@Id val isbn: Isbn, val title: Title)//現実 @Entity(immutable = true) public class Book(@Id val arg0: Isbn, val arg1: Title)
- KotlinでPluggable Annotation Processing APIが使えるkaptすごい
- Domaで使うにはあと一歩(注釈処理中に引数名がちゃんと取れたら完璧。なんか設定とかで出来るのであれば調査足らずですごめんちゃい)
- Kotlinかわいい
- Doma大好き
- Gradleぺろぺろ
https://github.com/backpaper0/sandbox/tree/master/kotlin-doma-sample