S-JIS[2020-10-08] 変更履歴

Doma2

Javaで使えるDoma2のメモ。


概要

Doma2はRDBのテーブルのカラムとJavaのクラスを紐付ける(マッピング)するライブラリー。(ORマッパー)
Java8以降。

Spring Bootで使われるJPA(Hibernate)ほど多機能ではなく、シンプル。


最小限コーディングする必要があるのは、Config・Entity・DAOクラス。

DAOインターフェースを記述すると、アノテーションプロセッサーによってDAOの実装クラスが生成される。


インストール

Doma2はMavenリポジトリーから取得できる。
が、Doma2ではアノテーションプロセッサー(apt)が使われるので、設定が少々ややこしい。(Eclipse等のIDEを使う場合、その設定が必要)
ドキュメントに従って、 雛形プロジェクトをインポートした方が良いと思われる。

build.gradleの例:

// https://github.com/domaframework/simple-boilerplate/blob/master/build.gradle
plugins {
    id 'java'
    id 'com.diffplug.eclipse.apt' version '3.24.0'
    id 'org.seasar.doma.compile' version '1.1.0'
}

java {
    sourceCompatibility = JavaVersion.VERSION_1_8
    targetCompatibility = JavaVersion.VERSION_1_8
}

compileJava {
    options.encoding = 'UTF-8'
}

compileTestJava {
    options.encoding = 'UTF-8'
}

repositories {
  mavenCentral()
}

dependencies {
 // https://mvnrepository.com/artifact/org.seasar.doma/doma-core
  implementation "org.seasar.doma:doma-core:2.41.0"
  annotationProcessor "org.seasar.doma:doma-processor:2.41.0"

  // https://mvnrepository.com/artifact/org.postgresql/postgresql
  compile group: 'org.postgresql', name: 'postgresql', version: '42.2.16'
  
  testCompile("org.junit.jupiter:junit-jupiter-api:5.7.0")
  testRuntime("org.junit.jupiter:junit-jupiter-engine:5.7.0")
  testRuntime("org.junit.platform:junit-platform-launcher:1.0.0") // for IDE
}

Configを継承した設定クラスを作成する。
どのサンプルもAppConfigという名前になっていた。

この例ではPostgreSQLをターゲットにしている。

AppConfig.java

package com.example.doma2.config;

import javax.sql.DataSource;

import org.seasar.doma.SingletonConfig;
import org.seasar.doma.jdbc.Config;
import org.seasar.doma.jdbc.dialect.Dialect;
import org.seasar.doma.jdbc.dialect.PostgresDialect;
import org.seasar.doma.jdbc.tx.LocalTransactionDataSource;
import org.seasar.doma.jdbc.tx.LocalTransactionManager;
import org.seasar.doma.jdbc.tx.TransactionManager;
// https://doma.readthedocs.io/en/2.20.0/transaction/
@SingletonConfig
public class AppConfig implements Config {

	private static final AppConfig CONFIG = new AppConfig();

	private final Dialect dialect;
	private final LocalTransactionDataSource dataSource;
	private final TransactionManager transactionManager;

	private AppConfig() {
		String url = "jdbc:postgresql://192.168.0.1:5432/hishidamadb";
		String user = "user";
		String password = "password";

		this.dialect = new PostgresDialect();
		this.dataSource = new LocalTransactionDataSource(url, user, password);
		this.transactionManager = new LocalTransactionManager(dataSource.getLocalTransaction(getJdbcLogger()));
	}

	@Override
	public Dialect getDialect() {
		return dialect;
	}

	@Override
	public DataSource getDataSource() {
		return dataSource;
	}

	@Override
	public TransactionManager getTransactionManager() {
		return transactionManager;
	}

	public static AppConfig singleton() {
		return CONFIG;
	}
}

ExampleEntity.java

package com.example.doma2.entity;

import org.seasar.doma.Column;
import org.seasar.doma.Entity;
import org.seasar.doma.Table;
@Entity
@Table(name = "example")
public class ExampleEntity {

	@Column(name = "example_column1")
	String exampleColumn1;

〜setter, getter〜
}

エンティティークラスがテーブルの1行分のデータを表す。

テーブル名はTableアノテーション、カラム名はColumnアノテーションで付ける。

セッターメソッドやゲッターメソッドは自分が使う場合だけコーディングすればよい。


ExampleDao.java

package com.example.doma2.dao;

import java.util.List;

import org.seasar.doma.Dao;
import org.seasar.doma.Select;
import org.seasar.doma.Sql;

import com.example.doma2.config.AppConfig;
import com.example.doma2.entity.ExampleEntity;
@Dao(config = AppConfig.class)
public interface ExampleDao {

	@Select
	@Sql("select * from example")
	List<ExampleEntity> selectAll();

	@Select
	@Sql("select * from example where example_column1 = /* id */'abc'")
	ExampleEntity selectByColumn1(String id);
}

DAOインターフェースにSQLを実行するメソッドを記述する。

SELECT文の場合はSelectアノテーションを付ける。
Doma2ではSELECT文の場合は必ずSQL文を指定する必要がある。Sqlアノテーションを付けてSQL文を書けるが、Sqlアノテーションを付けない場合はメソッド名と同名のファイルにSQL文を書く。
JPA(Hibernate)と異なり、メソッド名からSQL文を構築する機能は無い)

Doma2のSQL文では、メソッドの引数を使用することが出来る。
/* */で囲んで引数名を書く。その後ろに実際のSQLとして実行する際に使えるような値を書く。
これは、書かれたSQL文をコピペして直接実行する(試す)ことが出来るようにする為に、こうなっているようだ。
Doma2上で実行される際には、/* */の後ろの値は消され、メソッドの引数で指定された値が使われる。

DAOインターフェースをコンパイルすると、アノテーションプロセッサーによってDAOの実装クラス(上記の例だとExampleDaoImplクラス)が生成される。

なお、DaoImplクラスはスレッドセーフだそうだ


呼び出す例:

import org.seasar.doma.jdbc.tx.TransactionManager;

import com.example.doma2.entity.ExampleDao;
import com.example.doma2.entity.ExampleDaoImpl;
import com.example.doma2.entity.ExampleEntity;
	TransactionManager tm = AppConfig.singleton().getTransactionManager();

	ExampleDao dao = new ExampleDaoImpl();

	ExampleEntity entity = tm.required(() -> {
		return dao.selectByColumn1("zzz");
	});

TransactionManagerのrequiredメソッドでトランザクションを実行する。
requiredメソッドの引数のラムダ式でトランザクション内の処理を記述する。

SELECT文を実行する場合でも、トランザクションが必要。

requiredメソッド(のラムダ式)の中でスレッドを起こして個々のスレッドでSQL文を実行することは出来ない。( スレッドで実行するとTransactionNotYetBegunExceptionが発生する)
(LocalTransactionManagerはThreadLocalでスレッド毎にJDBCのConnectionを管理している。
 JDBCトランザクションはConnectionに対して実行するものであり、Connectionはマルチスレッドで扱えないので、TransactionManagerもマルチスレッドには対応できないと思われる)
参考: nakamura_toさんのツイート


Javaへ戻る / 技術メモへ戻る
メールの送信先:ひしだま