Domaとは、S2Daoのスタイル(DAOパターンや2 Way SQL)を踏襲したJava6(JDBC4.0)対応のO/Rマッパーで、Springフレームワークに組み込んで使うことができる。今回は、MyBatisの代わりにDomaを利用するよう変更してみたので、そのサンプルプログラムを共有する。
前提条件
下記記事の実装が完了していること。
サンプルプログラムの作成
作成したサンプルプログラムの構成は以下の通り。

なお、上記の赤枠は、前提条件のプログラムから追加/変更したプログラムである。
build.gradleの内容は以下の通りで、Domaのライブラリを追加すると共に、copySqlタスクを追加している。
plugins {
id 'org.springframework.boot' version '2.1.7.RELEASE'
id 'java'
}
apply plugin: 'io.spring.dependency-management'
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
compileOnly 'org.projectlombok:lombok:1.18.10'
annotationProcessor 'org.projectlombok:lombok:1.18.10'
compile files('lib/ojdbc6.jar')
// Domaのインストール
implementation 'org.seasar.doma.boot:doma-spring-boot-starter:1.4.0'
annotationProcessor 'org.seasar.doma:doma-processor:2.35.0'
}
// ビルド前に実行する、SQLファイルをクラスパスにコピーする処理
task copySql(type: Copy) {
from './src/main/resources/META-INF'
into './build/classes/java/main/META-INF'
}また、今回アクセスするテーブル(user_data)のエンティティクラスの内容は以下の通りで、テーブル名・カラム名や主キー設定を行っている。
package com.example.demo;
import lombok.Data;
import org.seasar.doma.Table;
import org.seasar.doma.Entity;
import org.seasar.doma.Id;
import org.seasar.doma.Column;
/**
* ユーザーデータテーブル(user_data)アクセス用エンティティ
*/
@Table(name = "user_data")
@Entity
@Data
public class UserData {
/** ID */
@Id
private long id;
/** 名前 */
private String name;
/** 生年月日_年 */
@Column(name="birth_year")
private int birthY;
/** 生年月日_月 */
@Column(name="birth_month")
private int birthM;
/** 生年月日_日 */
@Column(name="birth_day")
private int birthD;
/** 性別 */
private String sex;
}
さらに、今回アクセスするテーブル(user_data)のDaoインタフェースの内容は以下の通りで、「@ConfigAutowireable」「@Dao」アノテーションを先頭に付与し、各メソッドに「@Select」「@Delete」「@Update」「@Insert」アノテーションのいずれかを付与している。
package com.example.demo;
import org.seasar.doma.Dao;
import org.seasar.doma.Select;
import org.seasar.doma.Delete;
import org.seasar.doma.Insert;
import org.seasar.doma.Update;
import org.seasar.doma.boot.ConfigAutowireable;
import java.util.List;
@ConfigAutowireable
@Dao
public interface UserDataDao {
/**
* ユーザーデータテーブル(user_data)を全件取得する
* @return ユーザーデータテーブル(user_data)を全データ
*/
@Select
List<UserData> findAll();
/**
* 指定したIDをもつユーザーデータテーブル(user_data)のデータを取得する
* @param id ID
* @return ユーザーデータテーブル(user_data)の指定したIDのデータ
*/
@Select
UserData findById(Long id);
/**
* 指定したIDをもつユーザーデータテーブル(user_data)のデータを削除する
* @param userData ユーザーデータテーブル(user_data)の削除データ
*/
@Delete
int deleteById(UserData userData);
/**
* 指定したユーザーデータテーブル(user_data)のデータを追加する
* @param userData ユーザーデータテーブル(user_data)の追加データ
*/
@Insert
int create(UserData userData);
/**
* 指定したユーザーデータテーブル(user_data)のデータを更新する
* @param userData ユーザーデータテーブル(user_data)の更新データ
*/
@Update
int update(UserData userData);
/**
* ユーザーデータテーブル(user_data)の最大値IDを取得する
* @return ユーザーデータテーブル(user_data)の最大値ID
*/
@Select
long findMaxId();
}また、SQLファイルの内容は以下の通りで、Daoインタフェースで「@Select」アノテーションを付与したメソッドに対応するSQL文をそれぞれ記載している。
SELECT
id
, name
, birth_year
, birth_month
, birth_day
, sex
FROM USER_DATA
ORDER BY idSELECT
id
, name
, birth_year
, birth_month
, birth_day
, sex
FROM USER_DATA
WHERE id = /* id */1SELECT NVL(max(id), 0) FROM USER_DATA
なお、今回のサンプルでは使用していないが、「@Delete」「@Update」「@Insert」アノテーションでsqlFile属性をtrueにした場合も、SQLファイルが必要になる。
さらに、サービスクラスの実装クラスの内容は以下の通りで、先ほどのDaoインタフェースを呼び出すように修正している。
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.BindingResult;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@Service
public class DemoServiceImpl implements DemoService{
/**
* ユーザーデータテーブル(user_data)へアクセスするマッパー
*/
@Autowired
private UserDataDao userDataDao;
/**
* {@inheritDoc}
*/
@Override
public List<DemoForm> demoFormList() {
List<DemoForm> demoFormList = new ArrayList<>();
//ユーザーデータテーブル(user_data)から全データを取得する
Collection<UserData> userDataList = userDataDao.findAll();
for (UserData userData : userDataList) {
demoFormList.add(getDemoForm(userData));
}
return demoFormList;
}
/**
* {@inheritDoc}
*/
@Override
public DemoForm findById(String id) {
Long longId = stringToLong(id);
UserData userData = userDataDao.findById(longId);
return getDemoForm(userData);
}
/**
* {@inheritDoc}
*/
@Override
@Transactional(readOnly = false)
public void deleteById(String id){
UserData userData = new UserData();
userData.setId(stringToLong(id));
userDataDao.deleteById(userData);
}
/**
* {@inheritDoc}
*/
@Override
@Transactional(readOnly = false)
public void createOrUpdate(DemoForm demoForm){
//更新・追加処理を行うエンティティを生成
UserData userData = getUserData(demoForm);
//追加・更新処理
if(demoForm.getId() == null){
userData.setId(userDataDao.findMaxId() + 1);
userDataDao.create(userData);
}else{
userDataDao.update(userData);
}
}
/**
* {@inheritDoc}
*/
@Override
public String checkForm(DemoForm demoForm, BindingResult result, String normalPath){
//formオブジェクトのチェック処理を行う
if(result.hasErrors()){
//エラーがある場合は、入力画面のままとする
return "input";
}
//生年月日の日付チェック処理を行う
//エラーがある場合は、エラーメッセージ・エラーフィールドの設定を行い、
//入力画面のままとする
int checkDate = DateCheckUtil.checkDate(demoForm.getBirthYear()
, demoForm.getBirthMonth(), demoForm.getBirthDay());
switch(checkDate){
case 1:
//生年月日_年が空文字の場合のエラー処理
result.rejectValue("birthYear", "validation.date-empty"
, new String[]{"生年月日_年"}, "");
return "input";
case 2:
//生年月日_月が空文字の場合のエラー処理
result.rejectValue("birthMonth", "validation.date-empty"
, new String[]{"生年月日_月"}, "");
return "input";
case 3:
//生年月日_日が空文字の場合のエラー処理
result.rejectValue("birthDay", "validation.date-empty"
, new String[]{"生年月日_日"}, "");
return "input";
case 4:
//生年月日の日付が不正な場合のエラー処理
result.rejectValue("birthYear", "validation.date-invalidate");
//生年月日_月・生年月日_日は、エラーフィールドの設定を行い、
//メッセージを空文字に設定している
result.rejectValue("birthMonth", "validation.empty-msg");
result.rejectValue("birthDay", "validation.empty-msg");
return "input";
case 5:
//生年月日の日付が未来日の場合のエラー処理
result.rejectValue("birthYear", "validation.date-future");
//生年月日_月・生年月日_日は、エラーフィールドの設定を行い、
//メッセージを空文字に設定している
result.rejectValue("birthMonth", "validation.empty-msg");
result.rejectValue("birthDay", "validation.empty-msg");
return "input";
default:
//性別が不正に書き換えられていないかチェックする
if(!demoForm.getSexItems().keySet().contains(demoForm.getSex())){
result.rejectValue("sex", "validation.sex-invalidate");
return "input";
}
//エラーチェックに問題が無いので、正常時の画面遷移先に遷移
return normalPath;
}
}
/**
* DemoFormオブジェクトに引数のユーザーデータの各値を設定する
* @param userData ユーザーデータ
* @return DemoFormオブジェクト
*/
private DemoForm getDemoForm(UserData userData){
if(userData == null){
return null;
}
DemoForm demoForm = new DemoForm();
demoForm.setId(String.valueOf(userData.getId()));
demoForm.setName(userData.getName());
demoForm.setBirthYear(String.valueOf(userData.getBirthY()));
demoForm.setBirthMonth(String.valueOf(userData.getBirthM()));
demoForm.setBirthDay(String.valueOf(userData.getBirthD()));
demoForm.setSex(userData.getSex());
demoForm.setSex_value("1".equals(userData.getSex()) ? "男" : "女");
return demoForm;
}
/**
* UserDataオブジェクトに引数のフォームの各値を設定する
* @param demoForm DemoFormオブジェクト
* @return ユーザーデータ
*/
private UserData getUserData(DemoForm demoForm){
UserData userData = new UserData();
if(!DateCheckUtil.isEmpty(demoForm.getId())){
userData.setId(Long.valueOf(demoForm.getId()));
}
userData.setName(demoForm.getName());
userData.setBirthY(Integer.valueOf(demoForm.getBirthYear()));
userData.setBirthM(Integer.valueOf(demoForm.getBirthMonth()));
userData.setBirthD(Integer.valueOf(demoForm.getBirthDay()));
userData.setSex(demoForm.getSex());
return userData;
}
/**
* 引数の文字列をLong型に変換する
* @param id ID
* @return Long型のID
*/
private Long stringToLong(String id){
try{
return Long.parseLong(id);
}catch(NumberFormatException ex){
return null;
}
}
}その他のソースコード内容は、以下のサイトを参照のこと。
https://github.com/purin-it/java/tree/master/spring-boot-doma/demo
サンプルプログラムのビルド
サンプルプログラムをビルドするには、ビルド前にSQLファイルをクラスパスが通ったMETA-INFディレクトリ下に配置する必要がある。その手順は以下の通り。
1) IntelliJ IDEA上で右上「Gradle」タブをクリックし、Gradleタスクを表示する。

2) buildタスクを選択し右クリックし、「demo[build]の作成」メニューを押下する。

3) 以下の画面が開くため、起動前の「+」ボタンを押下し、「Gradleタスクの実行」メニューを選択する。

4) build.gradleに設定したcopySqlタスクを設定し、「OK」ボタンを押下する。

5) 起動前タスクにcopySqlタスクが設定されていることを確認後、「適用」ボタンを押下後、「OK」ボタンを押下する。

6) buildタスクを選択し右クリックし、「実行」メニューを押下する。

7) buildタスク実行時のコンソールログの内容は、以下の通り。


8) buildタスクによって、以下のビルド後ファイルが作成される。


サンプルプログラムの実行結果
Spring Bootアプリケーションを起動し、「http://(サーバー名):(ポート番号)/」とアクセスすると、以下の画面が表示される。

また、下記記事と同じように、データの追加・更新・削除を行うことができる。
要点まとめ
- Domaとは、S2Daoのスタイル(DAOパターンや2 Way SQL)を踏襲したJava6(JDBC4.0)対応のO/Rマッパーで、Springフレームワークに組み込んで使うことができる。
- Domaで実行するには、エンティティクラス、DAOインタフェース、SQLファイルが必要である。
- SQLファイルは、DAOインタフェースにおいて、SELECT文またはsqlFile=trueであるDML文で必要になる。
- SQLファイルは、「(クラスパスが通ったMETA-INFディレクトリ)/(実行するDaoのクラスパス)/(実行するDaoのメソッド名).sql」に記載する。






