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 id
SELECT id , name , birth_year , birth_month , birth_day , sex FROM USER_DATA WHERE id = /* id */1
SELECT 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」に記載する。