Doma2とは?Java向けORMの仕組みとDoma 3への移行判断
Doma2について調べると、公式ドキュメントとQiitaの導入手順が並びます。ただし2026年8月時点で最初に押さえるべきは、Doma 2系が2.62.1(2024年8月29日)を最後に更新を止め、その3日後に公開されたDoma 3が現行線になっているという事実です。「doma2」の検索1位は日本語版公式ドキュメントですが、そのURLは2.62.0にピン留めされたページです。この記事では、Domaの仕組み(注釈処理による型安全、DAOインターフェース、2WaySQL)を公式ドキュメントの記述に沿って整理したうえで、2系のまま保守するか3系へ上げるかの判断材料までまとめます。
まとめ
DomaはSQLを隠さないJava向けのデータベースアクセスフレームワークです。SQLは開発者が書き、注釈処理(アノテーションプロセッサ)がコンパイル時にSQLの構文・引数の対応・結果セットのマッピングを検証します。実行するまでSQLの誤りに気づけない一般的なORMとの差は、この検証タイミングにあります。
バージョンの現在地は明確です。2系は2.62.1(2024年8月29日)で止まり、Doma 3.0.0が2024年9月1日、最新は3.14.0(2026年5月9日)。2系はJava 8以降、3系はJava 17以降が必須です。Java 17に上げられるなら新規採用は3系が前提で、2系に留まる理由はJava 8縛りの既存資産だけです。
もうひとつ、日本語の解説記事に誤った記述が広く出回っている点にも触れておきます。Domaに@Transactionalや@Cacheableという注釈はありません。素のDomaではTransactionManagerのラムダでトランザクション境界を作り、Spring Boot連携ではSpring側の@Transactionalを使います。以下、それぞれの根拠を公式ドキュメントとソースにあたりながら見ていきます。
Doma2とは何か:SQLを隠さないデータベースアクセスフレームワーク
公式リポジトリはDomaを「DAO oriented database mapping framework for Java」と説明しています。ライセンスはApache License 2.0。2.62.1のREADMEは「他のライブラリに依存しない」と書いており、Maven Centralに置かれたdoma-core 3.14.0のpomにもdependencies要素はありません。「Doma2」はDomaの2系(2.x)を指す通称です。
コンパイル時にSQLとマッピングを検証する注釈処理
Domaの中核はdoma-processorです。javacの注釈処理として動き、@Daoを付けたインターフェースから実装クラスを生成します。生成の過程で検証されるのは、SQLテンプレートの構文、テンプレート内のバインド変数名とDAOメソッドの引数名の対応、そして結果セットとエンティティクラスのマッピング。対応が取れていなければコンパイルが失敗します。
つまりDomaが言う「型安全」は、Javaの型システムだけの話ではなく、ビルドを通った時点でSQLとJavaコードの接続が検証済みになるという意味です。SQLファイル名を打ち間違えても、バインド変数名を変え忘れても、テストを走らせる前にビルドが落ちます。
JPA・Hibernate・MyBatisとの役割分担
| フレームワーク | SQL | 誤りの検出 | 関連の扱い |
|---|---|---|---|
| Doma | 開発者が記述 | コンパイル時 | 3.4.0以降は集約戦略/以前は手動 |
| JPA / Hibernate | JPQLから自動生成 | 実行時 | @OneToMany等で標準装備 |
| MyBatis | 開発者が記述 | 実行時 | resultMapで定義 |
この表で注意したいのが右端です。@OneToManyや@ManyToOneはJPAの注釈であり、Domaには存在しません。Domaに宣言的な関連マッピングが入ったのは3.4.0(2025年2月8日)で、@AggregateStrategyを付けたインターフェースに@AssociationLinker付きの関数を並べ、任意のSELECT文の結果を集約へ組み立てる方式です。2系にこの3注釈はありません。2系で関連を組むなら、Criteria APIのassociateにラムダを渡して自分で詰め替えます。Java向けORM全体の選び方はJava ORM(O/Rマッパー)の種類と選び方|Hibernate・MyBatis・jOOQを比較【2026年版】で比較しています。
Doma 2とDoma 3の現在地と移行判断
2.62.1で止まった2系と、3.14.0まで進む3系
Maven CentralのメタデータとGitHubのリリース履歴を突き合わせると、2系の最終リリースは2.62.1で2024年8月29日、その3日後の2024年9月1日に3.0.0が出ています。以降2系のリリースはなく、2026年8月時点の最新は3.14.0(2026年5月9日)です。2年近く2系にはパッチが出ていない、という前提で判断する必要があります。
分岐点はJavaのバージョンです。2.62.1のREADMEは「Doma 2 is a database access framework for Java 8+.」、3.0.0のリリースノートは「Require Java 17 as a mandatory prerequisite」と書いています。Java 17へ上げられる環境なら3系一択。上げられないなら2系に留まる以外の選択肢はありませんが、それは「更新の止まったライブラリを使い続ける」判断なので、Javaのバージョン更新計画とセットで扱ってください。
Doma 3で削除されたAPIと移行で手を入れる箇所
公式のDoma 3.x Migration Guideが挙げる削除項目のうち、アプリケーションコードに直接効くのは次の3つです。
@SingletonConfigの削除。シングルトンのConfigは自分で書く@Daoのconfig要素の削除。Configはコンストラクタで実装クラスへ渡すNamingとNamingTypeのLENIENT_SNAKE_LOWER_CASE・LENIENT_SNAKE_UPPER_CASEの削除。SNAKE_LOWER_CASE・SNAKE_UPPER_CASEへ置き換える
残りはEntityType#getTableNameやStandardDialectのスペルミス修正といった、フレームワークを拡張していなければ触らない範囲です。DIコンテナがConfigを管理している構成なら、実質の作業はJava 17への引き上げとビルド設定の更新だけで済みます。SQLテンプレートの書式は2系と3系で変わっていないため、蓄積したSQLファイルは書き直し不要です。
導入手順:依存関係と注釈処理の設定
成果物はgroupId org.seasar.domaの2つです。doma-coreは実行時とコンパイル時の両方で必要、doma-processorは注釈処理を提供するためコンパイル時のみ必要です。Gradleでは公式がorg.domaframework.doma.compileプラグイン(3.0.1)の併用を推奨しています。
plugins {
id 'org.domaframework.doma.compile' version '3.0.1'
}
dependencies {
implementation 'org.seasar.doma:doma-core:3.14.0'
annotationProcessor 'org.seasar.doma:doma-processor:3.14.0'
}
Mavenの場合はmaven-compiler-pluginのannotationProcessorPathsにdoma-processorを並べます。公式サンプルは-Adoma.resources.dirというコンパイラ引数も付けており、これはEclipseでMavenプロジェクトを扱うときに必要だと注記されています。
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>17</source>
<target>17</target>
<annotationProcessorPaths>
<path>
<groupId>org.seasar.doma</groupId>
<artifactId>doma-processor</artifactId>
<version>3.14.0</version>
</path>
</annotationProcessorPaths>
<compilerArgs>
<arg>-Adoma.resources.dir=${project.basedir}/src/main/resources</arg>
</compilerArgs>
</configuration>
</plugin>
既存の資産を持たない新規プロジェクトなら、データベーススキーマからエンティティ・DAO・SQLファイルを生成するDoma CodeGen Plugin(Gradle・v3.2.2/2026年3月20日)を使う手もあります。Ant版のDoma-GenはMaven Centralでも2020年3月18日以降リリースがなく、公式ドキュメントが「no longer maintained」と明記しているため新規採用は避けてください。
エンティティクラスとDAOインターフェースの定義
エンティティのマッピング規則
エンティティは@Entityを付けたクラスです。テーブル名・カラム名の変換規則はnaming要素で指定し、明示したい場合だけ@Tableと@Columnで上書きします。主キーは@Id、自動採番は@GeneratedValueで、戦略はIDENTITY・SEQUENCE・TABLEの3種類です。
@Entity(naming = NamingType.SNAKE_LOWER_CASE)
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Integer id;
String employeeName;
@Version
Integer version;
// getter / setter は省略
}
採番で引っかかりやすい仕様が1つあります。@GeneratedValueで生成された値がフィールドへ代入されるのは、そのフィールドがnullか、0未満の値を持つときだけです。フィールドをプリミティブ型で宣言する場合は-1のような負の値で初期化しておかないと採番されません。SEQUENCE戦略を使うときは@SequenceGeneratorを併記し、シーケンス名や割り当てサイズをデータベース側の定義と一致させる必要があります。エンティティクラスという設計要素そのものの位置づけはエンティティクラスとは|JPAとEF Coreの書き方・DTOとの違いを実例で解説で扱っています。
DAOインターフェースと生成される実装クラス
DAOはクラスではなくインターフェースとして定義します。@Daoを付けると、同名にImplを付けた実装クラスがコンパイル時に生成される仕組みです。Doma 3ではConfigをコンストラクタで渡します。
@Dao
public interface EmployeeDao {
@Select
Employee selectById(Integer id);
@Update
int update(Employee employee);
}
// 呼び出し側
EmployeeDao dao = new EmployeeDaoImpl(config);
Employee employee = dao.selectById(1);
DAOインターフェースとエンティティクラスを1対1に対応させる必要はありません。公式ドキュメントも「1つのDAOインターフェースが複数のエンティティクラスを扱ってよい」としています。DIコンテナで管理する構成では、実装クラスの生成をコンテナに任せます。ビジネスロジックとDAOの間に置くレイヤの切り方はサービスクラスとは?Javaでの役割・書き方とコントローラーとの違いを参照してください。
SQLテンプレート(2WaySQL)の配置規約とディレクティブ
SQLファイルのパス規約とRDBMS別の切り替え
Domaが読むSQLファイルのパスはMETA-INF/(DAOインターフェースの完全修飾名をスラッシュ区切りにしたパス)/(DAOメソッド名).sqlという形式で固定されています。DAOがaaa.bbb.EmployeeDao、メソッドがselectByIdなら、パスはMETA-INF/aaa/bbb/EmployeeDao/selectById.sqlです。「META-INF/sqlフォルダにまとめて置く」という説明を見かけますが誤りで、パッケージ構成がそのままディレクトリ構成になります。ファイルのエンコーディングはUTF-8必須です。
データベース固有のSQLを書き分けたいときは、拡張子の前にハイフンとRDBMS名を挟みます。selectById-postgres.sqlが存在すれば、PostgreSQL環境では汎用のselectById.sqlより優先されます。使える名前は方言クラスに対応したdb2・h2・hsqldb・mssql・mysql・oracle・postgres・sqliteです。
なお、SQLファイルを読むのは@Selectと、sqlFile = trueを明示した@Insert・@Update・@Delete・@BatchInsert・@BatchUpdate・@BatchDeleteだけです。指定しない更新系はエンティティ定義からSQLが自動生成されます。
条件分岐・繰り返し・列展開のディレクティブ
2WaySQLと呼ばれるのは、テンプレートがそのままSQLツールで実行できる形を保つからです。バインド変数は/* id */0のようにコメントとテストデータの組で書きます。実行時にはコメントと直後のテストデータがまとめて?へ置き換わり、0のほうはSQLツールで構文確認するためだけに存在します。
select /*%expand*/* from employee
where
/*%if employeeName != null */
employee_name like /* @prefix(employeeName) */'A%' escape '$'
/*%end*/
/*%for id : ids */
or employee_id = /* id */0
/*%end*/
前方一致で使う@prefix関数は、値に含まれる%や_を既定のエスケープ文字$で退避します(AB%CならAB$%C%)。そのためLIKE句にはescape '$'を付けないと、$がリテラルとして扱われて前方一致が壊れます。公式ドキュメントの例も必ずこの句を伴っています。
公式が挙げるディレクティブは8種類です。バインド変数/* */、リテラル変数/*^ */、埋め込み変数/*# */、条件分岐/*%if*/(/*%elseif*/・/*%else*/・/*%end*/を伴う)、繰り返し/*%for 変数 : コレクション*/(ループ内で変数_indexと変数_has_nextが使えます)、SELECT句の列展開/*%expand*/、UPDATE SET句の展開/*%populate*/、そして解析時に取り除かれるコメント/*%! */です。
このうち埋め込み変数とリテラル変数は値をSQLへ直接埋め込みます。埋め込み変数はORDER BY句のように列名をバインド変数にできない箇所で使い、Domaはシングルクォート・セミコロン・二重ハイフン・スラッシュとアスタリスクの並びを含む文字列を拒否します。リテラル変数はバインド変数を避けて実行計画を固定したいときに使い、こちらはエスケープを一切行わずシングルクォートだけを拒否します。いずれも列名や値のホワイトリスト検証はアプリケーション側の責任です。動的SQLの組み立て方はMyBatis Dynamic SQL(動的SQL)の書き方|if・foreach・whereとインジェクション対策と比べると設計思想の違いが見えます。
付け加えると、他のSQLファイルを取り込む#includeのようなディレクティブはDomaに存在しません。共通句を再利用したい場合は、@Sql注釈でテキストブロックにSQLを書いてJava側で組み立てるか、次に触れるQuery DSLを使います。
Query DSLによる動的SQLの組み立てとメタモデル
SQLファイルを持たずにJavaコードでクエリを組む経路もあります。Doma 3のUnified Criteria API(QueryDsl)は、2系にあったEntityqlとNativeSqlという2つのDSLを1つに統合したもので、3.1.0(2024年11月4日)で追加されました。3.0.0にはまだ入っていません。旧2つはClassic Criteria APIとして残っていますが、公式ドキュメントはQueryDslの使用を案内しています。
QueryDsl queryDsl = new QueryDsl(config);
var e = new Employee_();
List<Employee> list = queryDsl.from(e)
.where(c -> c.eq(e.departmentId, 10))
.fetch();
ここで使うEmployee_はメタモデルクラスで、エンティティに@Entity(metamodel = @Metamodel)を付けると注釈処理が生成します。カラム名を文字列で書かずに済むため、リファクタリングをコンパイラが追えます。条件の組み替えが多い検索はQuery DSL、複雑な結合や集計は可読性の点でSQLテンプレート、という使い分けになります。なお名前が似ていますが、QueryDSLとは?Spring Bootでの導入手順と5.1.0・OpenFeign版7.5の選び方で扱っているQuerydslはDomaとは無関係の別ライブラリです。
トランザクション制御とSpring Boot連携
素のDomaのトランザクション境界:TransactionManagerのラムダ
Doma単体で使う場合、トランザクション境界は注釈ではなくAPIで作ります。Config実装のgetDataSourceがLocalTransactionDataSourceを返し、そのローカルトランザクションから作ったLocalTransactionManagerで制御する構成です。
TransactionManager tm = DbConfig.singleton().getTransactionManager();
EmployeeDao dao = new EmployeeDaoImpl(DbConfig.singleton());
tm.required(() -> {
Employee employee = dao.selectById(1);
employee.setEmployeeName("hoge");
dao.update(employee);
});
開始方法はrequired(既存があれば流用、なければ新規)、requiresNew(既存を中断して常に新規)、notSupported(既存を中断してトランザクション外で実行)の3つ。ラムダが正常終了すればコミット、例外が飛べばロールバックされます。例外を投げずに取り消したいときはsetRollbackOnly、一部だけ戻したいときはsetSavepointで名前を付けてrollbackに渡します。JTAによるグローバルトランザクションを使う場合は、Domaではなくアプリケーションサーバ側の機能に任せる設計になります。
ここが日本語の解説記事で最も誤りが多い箇所です。org.seasar.doma直下の注釈は3.14.0で67個、2.62.1で62個ありますが、そのどちらにもTransactional・Cacheable・CacheEvictは含まれていません。DAOメソッドに@Transactionalや@Cacheableを付ける例を載せた記事は、Springの注釈をDomaのものと取り違えています。
Spring Boot連携でのトランザクション境界と設定プロパティ
doma-spring-boot-starterを入れると、DomaのConfigがSpringのデータソースとトランザクション管理に接続されます。この構成では境界の宣言はSpring側の@Transactionalが担い、DomaのLocalTransactionManagerは使いません。最新の3.0.0(2025年11月25日公開)の要件はJava 17以降・Doma 3.x以降・Spring Boot 4.x以降です。
doma.dialect=postgres
doma.naming=snake_lower_case
doma.exception-translation-enabled=true
設定はapplication.propertiesのdoma.接頭辞で行います。doma.exception-translation-enabledは既定でtrueで、DomaのJdbcExceptionをSpringのDataAccessExceptionへ変換します。SQLファイルのキャッシュ方針を決めるdoma.sql-file-repositoryの既定はGREEDY_CACHEです。META-INFにdoma.propertiesを置いて接続情報を書く、という設定方式はDomaに存在しません。設定はConfig実装か、Spring Bootではこのプロパティ群で行います。
DomaはSQLの発行タイミングが明示的で遅延ロードの仕組みも持たないため、JPAで描画時に追加クエリが飛んで問題になる構図は発生しません。JPA側でその境界がどう問題になるかはspring.jpa.open-in-viewとは?OSIVの既定値trueと起動時警告の消し方・falseの判断基準にまとめています。
Domaを採用すべきでない条件
Domaが向かない場面を先に挙げます。3つのいずれかに当てはまるなら、他の選択肢と比べたほうが早く決まります。
最も重いのが1つ目、Java 17へ上げられず、かつ長期運用が前提の新規プロジェクトです。この条件で使えるのは2系だけですが、2系は2024年8月29日以降リリースがありません。脆弱性が見つかっても上流の修正が来ない前提で、自前でフォークして直す体制まで用意できるかを先に決める必要があります。用意できないなら、Java 8で動く他のフレームワークか、Javaのバージョン更新そのものを計画に載せるほうが安全です。
2つ目は、1つのコードベースで複数のRDBMSを本番稼働させる製品です。SQLを自分で書く方針である以上、方言差は-mysql.sql・-postgres.sqlのファイル分岐で吸収することになり、対応DBの数だけSQLファイルが増えます。SQLを抽象化するJPAのほうが総量は小さく済みます。
3つ目は、単純なCRUDが大半で立ち上がりの速さを優先する小規模開発です。注釈処理のビルド設定とSQLファイルのパス規約という初期コストに、Spring Data JPAのメソッド名からのクエリ生成が見合いません。逆に言えば、SQLのチューニングが性能要件に直結する業務システムや、複雑な結合と集計を書き続ける領域が、Domaが最も効く場所です。
よくある質問
Q. DomaとDoma2は別のものですか。
A. 同じものです。「Doma2」はバージョン2系(2.x)を指す通称で、日本語版公式ドキュメントも2系のページのタイトルを「Doma 2へようこそ」としています。単に「Doma」と書かれた最新の資料は3系を指しています。
Q. DAOクラスとは何ですか。Domaではどう書きますか。
A. DAO(Data Access Object)はデータベースアクセスを集約する設計要素です。Domaではクラスではなくインターフェースとして@Dao付きで定義し、実装クラスは注釈処理が生成するため自分では書きません。
Q. DomaはJavaのORマッパーですか。
A. データベースの行をJavaオブジェクトへ対応付けるという意味ではORマッパーですが、SQLを自動生成しない点で一般的なORMとは分担が異なります。公式は「DAO oriented database mapping framework」と表現しています。HibernateやMyBatisとの比較はJava ORM(O/Rマッパー)の種類と選び方|Hibernate・MyBatis・jOOQを比較【2026年版】にまとめています。
Q. SQLファイルの場所を間違えると何が起きますか。
A. 実行時ではなくコンパイル時にエラーになります。パスの規約は前述のとおりDAOの完全修飾名がそのままディレクトリ構成になるため、DAOのパッケージを移動したらSQLファイルも一緒に移動させてください。
Q. Doma 3に上げるとSQLファイルは書き直しになりますか。
A. なりません。作業はJava 17への引き上げと、前述した削除API3点の置き換えだけです。