JavaO/RマッパーであるDomaを試してみる
Doma の Tutorial をやってみる
公式のチュートリアルが丁寧なので、それを見るのがよいです。
http://doma.seasar.org/setup.html
概要
重要っぽいところだけ抜粋(主に利用するユーザとしても視点で)
SQL ファイルの作り方とメソッドの実装方法がポイントな気がします。
実際にやってみた
ポイントだけメモします。
注釈処理(Annotation Processing)の設定
有効にして、ファクトリーパスに doma01.38.0.jar を追加します。デフォルトだとパッケージ・エクスプローラに生成される Java ファイルが表示されないのでフィルターを変更しておきます。
フォルダ構成
フォルダ | 説明 |
---|---|
src/main/java | 設定クラス、Daoインタフェース、エンティティクラス等のソースフォルダ |
src/main/resources | QLファイル等のソースフォルダです。 SQLファイルは、Domaの規約に則ってMETA-INFフォルダ以下に配置されます。 また、JDBCドライバのプロバイダ構成ファイルがMETA-INF/servicesフォルダ以下にjava.sql.Driverという名前で配置されます。 |
src/test/java | Daoを利用するコードのためのソースフォルダです。 Daoを利用するコードはすべてJUnitのテストクラスとして記述されています。 |
src/test/resources | 使用していません。 |
.apt_generated | apt(Annotation Processing Tool)によって生成されたコードを格納するソースフォルダです。 |
pom はこんな感じ
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>sample</groupId> <artifactId>sample</artifactId> <version>0.0.1-SNAPSHOT</version> <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <dependencies> <!-- https://mvnrepository.com/artifact/postgresql/postgresql --> <dependency> <groupId>postgresql</groupId> <artifactId>postgresql</artifactId> <version>9.1-901.jdbc4</version> </dependency> </dependencies> </project>
Maven の Doma の artifactId を pom に記載したところ、Missing artifact org.seasar.doma:doma-tool:jar:0.9.1
となったので手動で jar を追加しました。
<!-- https://mvnrepository.com/artifact/org.seasar.doma/doma-tool --> <dependency> <groupId>org.seasar.doma</groupId> <artifactId>doma-tool</artifactId> <version>0.9.1</version> </dependency>
データベースは H2 ではなく PostgreSQL を使いました
docker run --name my-db -p 5432:5432 -e POSTGRES_USER=dev -e POSTGRES_PASSWORD=dev -d postgres:9.6
インスタンス化する Dialect
を切り替えて、 createDataSource()
の設定を修正するだけです。
public class AppConfig extends DomaAbstractConfig { protected static final LocalTransactionalDataSource dataSource = createDataSource(); protected static final Dialect dialect = new PostgresDialect(); @Override public DataSource getDataSource() { return dataSource; } @Override public Dialect getDialect() { return dialect; } protected static LocalTransactionalDataSource createDataSource() { SimpleDataSource dataSource = new SimpleDataSource(); dataSource.setUrl("jdbc:postgresql:dev://localhost:5432"); dataSource.setUser("dev"); dataSource.setPassword("dev"); return new LocalTransactionalDataSource(dataSource); } public static LocalTransaction getLocalTransaction() { return dataSource.getLocalTransaction(defaultJdbcLogger); } }
実行
Main.java を実行すると以下のように出力されました。
情報: [DOMA2063] ローカルトランザクション[233530418]を開始しました。 [土 3 23 18:15:24 JST 2019] 情報: ENTRY [土 3 23 18:15:24 JST 2019] 情報: [DOMA2076] SQLログ : SQLファイル=[META-INF/example/EmployeeDao/selectById.sql], select * from EMPLOYEE where EMPLOYEE_ID = 3 [土 3 23 18:15:24 JST 2019] 情報: RETURN example.Employee@27f674d [土 3 23 18:15:24 JST 2019] 情報: ENTRY [土 3 23 18:15:24 JST 2019] 情報: [DOMA2076] SQLログ : SQLファイル=[null], update EMPLOYEE set EMPLOYEE_NAME = 'HOMU', HIREDATE = '1981-02-22', SALARY = 4550.00, VERSION_NO = 3 + 1 where EMPLOYEE_ID = 3 and VERSION_NO = 3 [土 3 23 18:15:24 JST 2019] 情報: RETURN 1 [土 3 23 18:15:24 JST 2019] 情報: [DOMA2067] ローカルトランザクション[233530418]をコミットしました。 [土 3 23 18:15:24 JST 2019] 情報: [DOMA2064] ローカルトランザクション[233530418]を終了しました。 [土 3 23 18:15:24 JST 2019]
チュートリアルでの主要なクラス
設定クラス
エンティティクラス
テーブルやSQLの結果セットにマッピングされたクラスで @Entity
を注釈して示します。テーブルの主キーには @Id
を注釈します。識別子を自動生成する場合は @GeneratedValue
を用いることができます。
例
@Entity(listener = EmployeeListener.class) public class Employee { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) @SequenceGenerator(sequence = "EMPLOYEE_SEQ") Integer id; String name; int age; Salary salary; @Column(name = "JOB_TYPE") JobType jobType; Date hiredate; @Column(name = "DEPARTMENT_ID") Integer departmentId; @Version @Column(name = "VERSION") Integer version; Timestamp insertTimestamp; Timestamp updateTimestamp; @OriginalStates Employee originalStates; ... }
エンティティリスナークラス
エンティティリスナークラスのインスタンスは、エンティティがデータベースに挿入、更新、削除される前後に呼び出されます。
ドメインクラス
Dao インターフェース
Dao インターフェースは @Dao
を用いて示します。 @Dao(config = )
には設定クラスである AppConfig クラスを指定します。すべてのメソッドには @Select
や @Update
などのクエリの種別を示すアノテーションが必要です。
@Dao(config = AppConfig.class) public interface EmployeeDao { @Select Employee selectById(Integer id); @Select List<Employee> selectByAgeRange(Integer min, Integer max); @Select List<Employee> selectByAges(List<Integer> ages); @Select List<Employee> selectByNames(List<String> names); ... @Insert int insert(Employee employee); @Insert(sqlFile = true) int insertWithSqlFile(Employee employee); @Update int update(Employee employee); @Update(sqlFile = true) int updateWithSqlFile(Employee employee); @Delete int delete(Employee employee); @Delete(sqlFile = true) int deleteWithSqlFile(Employee employee); ... }
SQL ファイル
src/main/resources/META-INF/tutorial/EmployeeDao 配下に配置されます。 フォルダの名前は Dao インタフェースと対応づけられます。SQL ファイルは、名前から拡張子を除いたものが Dao インタフェースのメソッドに一致します。たとえば、selectById.sql は Dao インタフェースの selectById メソッドに対応します。 selectById.sql の中身は次のようなテキストです。
select * from employee where id = /* id */0
所感
「暗黙的な規約よりも明示的な設定を重視する」というコンセプトのとおり、アノテーションを使って明示的に RDB にアクセスするようなフレームワークに感じた。 フレームワークのお作法に則って開発すればシンプルな実装で RDB へのアクセスができそうな気がしている。