はじめに
この記事では、データベースアクセスフレームワークである Doma を利用する際の開発環境構築についてノウハウを紹介します。
簡単なサンプルアプリケーションを作りながら解説します。
前提
バージョンなど
開発マシンは macOS を想定しますが、Windows と異なるところはないと思います。
IDE、ツール、ライブラリ等のバージョンは以下のとおりです。
- Eclipse IDE for Java Developers 2024-03 (4.31.0)
- Java 17.0.10(Eclipse のデフォルト)
- Maven 3.9.6(Eclipse のデフォルト)
- Doma 2.60.0
Eclipse の仕組みを踏まえた戦略
Eclipse は以下のようなさまざまな設定ファイルを参照して動きます。
- .classpath : どのライブラリのどのバージョンを使うのかといったクラスパス情報
- .factorypath : アノテーションプロセッサーのライブラリのパス情報
- .project : プロジェクト名などのメタ情報
- .settings : Eclipseの画面からの設定項目など複数ファイルを管理するフォルダ
これらのファイルは Eclipse の 画面上で設定を行うことで出力できますが、その場合の問題点はビルドファイルと Eclipse の設定ファイルで二重管理が必要となってしまうことです。 二重管理を避けるために、ビルドファイルを一次情報としてビルドファイルから Eclipse の設定ファイルを自動生成するのが基本的な戦略となります。
Eclipse から Maven Project の作成
Eclipse 上部のメニューから File > New > Maven Project を選択しダイアログを開きます。
Create a simple project にチェックを入れ、 Next ボタンを押します。
Group Id と Artifact Id のどちらにも example を入れ、Finish ボタンを押します。
そうすると、example プロジェクトが作成されます。
Annotation Processing の有効化
example プロジェクトを右クリックして Properties > Maven > Annotaion Processing を選択し、 Enable Project specific settings にチェックを入れ、 Automatically configure JDT APT にもチェックを入れます。
この設定を有効にしないと、pom.xml から .factorypath など Eclipseのアノテーションプロセッシングに必要なファイルが生成されません。必ず有効にしましょう。
生成された pom.xml の確認
example プロジェクトの pom.xml を確認しましょう。
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>example</groupId>
<artifactId>example</artifactId>
<version>0.0.1-SNAPSHOT</version>
</project>
pom.xml の修正
オリジナルの pom.xml を以下の内容で置換します。
<?xml version="1.0" encoding="UTF-8"?>
<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>example</groupId>
<artifactId>example</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<doma.version>2.60.0</doma.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.seasar.doma</groupId>
<artifactId>doma-core</artifactId>
<version>${doma.version}</version>
</dependency>
<dependency>
<groupId>org.seasar.doma</groupId>
<artifactId>doma-slf4j</artifactId>
<version>${doma.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.11</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>2.2.224</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>17</source> <!-- depending on your project -->
<target>17</target> <!-- depending on your project -->
<annotationProcessorPaths>
<path>
<groupId>org.seasar.doma</groupId>
<artifactId>doma-processor</artifactId>
<version>${doma.version}</version>
</path>
</annotationProcessorPaths>
<compilerArgs>
<!-- if you are using a Maven project in Eclipse, this argument is required -->
<arg>-Adoma.resources.dir=${project.basedir}/src/main/resources</arg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
</project>
ポイントは、 compilerArgs にリソースフォルダを指定することです。これは、注釈処理時にSQLファイルを参照するために必要です。
Doma を利用するコードの追加
example プロジェクトの src/main/java フォルダの example パッケージに次のクラスを追加します。
package example;
import java.util.Objects;
import javax.sql.DataSource;
import org.seasar.doma.jdbc.Config;
import org.seasar.doma.jdbc.JdbcLogger;
import org.seasar.doma.jdbc.dialect.Dialect;
import org.seasar.doma.jdbc.dialect.H2Dialect;
import org.seasar.doma.jdbc.tx.LocalTransactionDataSource;
import org.seasar.doma.jdbc.tx.LocalTransactionManager;
import org.seasar.doma.jdbc.tx.TransactionManager;
import org.seasar.doma.slf4j.Slf4jJdbcLogger;
public class DbConfig implements Config {
private final Dialect dialect;
private final DataSource dataSource;
private final JdbcLogger jdbcLogger;
private final TransactionManager transactionManager;
public DbConfig(Dialect dialect, DataSource dataSource, JdbcLogger jdbcLogger,
TransactionManager transactionManager) {
this.dialect = Objects.requireNonNull(dialect);
this.dataSource = Objects.requireNonNull(dataSource);
this.jdbcLogger = Objects.requireNonNull(jdbcLogger);
this.transactionManager = Objects.requireNonNull(transactionManager);
}
@Override
public JdbcLogger getJdbcLogger() {
return jdbcLogger;
}
@Override
public Dialect getDialect() {
return dialect;
}
@Override
public DataSource getDataSource() {
return dataSource;
}
@Override
public TransactionManager getTransactionManager() {
return transactionManager;
}
public static DbConfig of() {
var dialect = new H2Dialect();
var dataSource = new LocalTransactionDataSource("jdbc:h2:mem:tutorial;DB_CLOSE_DELAY=-1", "sa", null);
var jdbcLogger = new Slf4jJdbcLogger();
var transactionManager = new LocalTransactionManager(dataSource, jdbcLogger);
return new DbConfig(dialect, dataSource, jdbcLogger, transactionManager);
}
}
package example;
import org.seasar.doma.Entity;
import org.seasar.doma.Id;
@Entity
public class Person {
@Id
Integer id;
String name;
}
package example;
import java.util.List;
import org.seasar.doma.Dao;
import org.seasar.doma.Insert;
import org.seasar.doma.Script;
import org.seasar.doma.Select;
import org.seasar.doma.Sql;
@Dao
public interface PersonDao {
@Script
@Sql("""
create table if not exists person(
id integer not null primary key,
name varchar(20)
)
""")
void createTable();
@Select
List<Person> selectAll();
@Insert
int insert(Person person);
}
example プロジェクトの src/main/resources/META-INF/example/PersonDao フォルダに次の SQL ファイルを追加します。
select /*%expand*/* from person
example プロジェクトの src/test/java フォルダの example パッケージに次のクラスを追加します。
package example;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
public class PersonDaoTest {
// データベースの設定
DbConfig dbConfig = DbConfig.of();
// DAO
PersonDao dao = new PersonDaoImpl(dbConfig);
@Test
public void test() {
// トランザクション内で実行
dbConfig.getTransactionManager().required(() -> {
dao.createTable(); // テーブルの作成
var person1 = new Person();
person1.id = 1;
person1.name = "abc";
dao.insert(person1); // 追加
var person2 = new Person();
person2.id = 2;
person2.name = "def";
dao.insert(person2); // 追加
var people = dao.selectAll(); // 全件取得
assertEquals(2, people.size());
for (var p : people) {
System.out.printf(p.name);
}
});
}
}
動作確認(Doma 関連の設定が正しいか?)
追加した Doma のコードのビルドや実行を確認します。
クリーンビルドの実行
Eclipse の上部メニューから Project > Clean を実行して問題が発生しないことを確認します。
JUnit の実行
example プロジェクトを右クリックして Run As > JUnit Test を選択します。
JUnit ビューから PersonDaoTest が実行されていること、グリーンのバーが表示されていることを確認します。
アノテーションプロセッサーによる検証
PersonDao.java の @Insert
をコメントアウトし、ファイルを保存します。
エディタ上に下記のエラーメッセージが表示されたらアノテーションプロセッサーが適切に動いていると言えます。
[DOMA4005] The query annotation such as @Select and @Update is required.
確認を終えたらコメントアウトを元に戻してください。
ファイル保存時にアノテーションプロセッサーを起動するには、 Eclipse 上部の Project メニューで Build Automatically が有効になっていなければいけません(デフォルトで有効です)。無効にしている場合は、代わりにクリーンビルドを実行してください。
Q & A
Maven > Update Project をいつ実行すればいいのですか?
明示的にこのコマンドを実行しなくてもデフォルトで pom.xml の変更が自動的にプロジェクトに反映されるようですが、必要に応じて、プロジェクトや pom.xml を右クリックして Maven > Update Project を実行してください。
アノテーションプロセッサーのオプションを指定するにはどうすればいいですか?
次のような記述が必要です。Domaのドキュメントも参照ください。
<compilerArgs>
<arg>-Adoma.resources.dir=${project.basedir}/src/main/resources</arg>
<arg>-Adoma.dao.subpackage=impl</arg>
<arg>-Adoma.dao.suffix=Impl</arg>
</compilerArgs>
Eclipse の設定ファイルを確認するにはどうするのが良いですか?
Eclipse の Package Explorer の右上メニューから Filters... を選択し、 .*resources のチェックを外せば Package Explorer 上で表示できます。
Visual Studio Code などで表示するのはオススメしません。Visual Studio Code に Java の拡張機能が入っていると勝手に Eclipse の設定を書き換えたりすることがあるので注意が必要です。
おわりに
もっと良い方法をご存知の方は共有をよろしくお願いします。
Maven ではなく Gradle を利用する場合は、Eclipse + Java + Gradle の環境で Doma を動かす を参照ください。