MyBatis Dynamic SQLを使ってみた。
背景
Java + MyBatisを使って開発を行っているのですが、コード上でSQLの組み立てを行う方法が無いか調べたところ、MyBatis Dynamic SQL
を使うと実現できる様です。
以下のQuick Startを元に試してみたので、ここではその際のメモを残そうと思います。
MyBatis Dynamic SQL – MyBatis Dynamic SQL Quick Start
ハンズオン
用意するクラス, インターフェース
- Supportクラス(テーブル, カラムの定義を行う。)
- Mapperインタフェース
- Mapperを呼び出すクラス
- Entityクラス
実装例
コード全体は以下より取得可能です。
GitHub - U0326/code-example-mybatis_dynamic_sql
事前準備
CREATE TABLE friends ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(10) );
上記の通り作成したテーブルを利用します。
Supportクラス
public class FriendDynamicSqlSupport { public static final Friend friend = new Friend(); // 1 public static final SqlColumn<Integer> id = friend.id; // 1 public static final SqlColumn<String> mame = friend.name; // 1 public static final class Friend extends SqlTable { // 2 public final SqlColumn<Integer> id = column("id", JDBCType.INTEGER); public final SqlColumn<String> name = column("name", JDBCType.VARCHAR); public Friend() { super("friends"); //3 } } }
- 2で作成する内部クラスとその変数をstatic参照可能にする為に、
public static
な変数を定義する。 SqlTable
を継承した内部クラスを作成し、カラム定義を行う。[スキーマ名.]テーブル名
を渡してSqlTable
のコンストラクタを呼び出す。
Mapperインターフェース
上記で作成したSupportクラスをstatic importし、以下の通りCRUD操作を定義します。
Create
@Mapper public interface FriendMapper { // Create @InsertProvider(type = SqlProviderAdapter.class, method = "insert") // 1 int insert(InsertStatementProvider<FriendEntity> insertStatement); // 2 default int insert(FriendEntity entity) { // 3 return MyBatis3Utils.insert(this::insert, entity, friend, c -> c.map(mame).toProperty("name")); // 4 } ... }
- Createを行うメソッドであることを、アノテーションを用いて宣言する。
InsertStatementProvider<Entityクラス>
を引数に取るメソッドを定義する。MyBatis3Utils
を用いたdefaultメソッドを定義し、呼び元でEntityクラスをのみを引数にしたCreateを可能にする。- 以下の通り引数を渡し、
MyBatis3Utils.insert
を呼び出す。- 第3引数: Supportクラス内の
SqlTable
を継承したクラスのstatic参照 - 第4引数: Supportクラス内の
SqlColumn
のstatic参照と、Entityクラスの変数名を以下の通り紐付けるラムダ式
c -> c.map(SqlColumnのstatic参照).toProperty("Entityクラスの変数名")
- 第3引数: Supportクラス内の
Read
@Mapper public interface FriendMapper { ... // Read @SelectProvider(type = SqlProviderAdapter.class, method = "select") // 1 @Results( // 2 { @Result(column = "id", jdbcType = JdbcType.INTEGER, property = "id", id = true), @Result(column = "name", jdbcType = JdbcType.VARCHAR, property = "name"), }) Optional<FriendEntity> selectOne(SelectStatementProvider selectStatement); // 3 default Optional<FriendEntity> selectOne(SelectDSLCompleter completer) { // 4 return MyBatis3Utils.selectOne(this::selectOne, selectList, friend, completer); // 5 } BasicColumn[] selectList = BasicColumn.columnList(id, name); // 6 ... }
- Readを行うメソッドであることを、アノテーションを用いて宣言する。
- カラム名とEntityクラスの変数名を以下の通り紐付ける。
@Result(column = "カラム名", jdbcType = カラムの型, property = "Entityクラスの変数名")
SelectStatementProvider
を引数に取るメソッドを定義する。MyBatis3Utils
を用いたdefaultメソッドを定義し、呼び元でSELECT対象のカラムを指定せずReadを可能にする。- 以下の通り引数を渡し、
MyBatis3Utils.selectOne
を呼び出す。- 第2引数: 6で定義するSELECT対象のカラムの配列
- 第3引数: Supportクラス内のSqlTableを継承したクラスのstatic参照
- Supportクラス内の
SqlColumn
のstatic参照を用いて、SELECT対象のカラムの配列を定義する。
Update
@Mapper public interface FriendMapper { ... // Update @UpdateProvider(type = SqlProviderAdapter.class, method = "update") // 1 int update(UpdateStatementProvider updateStatement); // 2 default int update(UpdateDSLCompleter completer) { // 3 return MyBatis3Utils.update(this::update, friend, completer); // 4 } ... }
- Updateを行うメソッドであることを、アノテーションを用いて宣言する。
UpdateStatementProvider
を引数に取るメソッドを定義する。MyBatis3Utils
を用いたdefaultメソッドを定義し、呼び元でテーブル名を指定せずにUpdateを可能にする。- 以下の通り引数を渡し、
MyBatis3Utils.update
を呼び出す。- 第2引数: Supportクラス内のSqlTableを継承したクラスのstatic参照
Delete
@Mapper public interface FriendMapper { ... // Delete @DeleteProvider(type = SqlProviderAdapter.class, method = "delete") // 1 int delete(DeleteStatementProvider deleteStatement); // 2 default int delete(DeleteDSLCompleter completer) { // 3 return MyBatis3Utils.deleteFrom(this::delete, friend, completer); // 4 } ... }
- Deleteを行うメソッドであることを、アノテーションを用いて宣言する。
DeleteStatementProvider
を引数に取るメソッドを定義する。MyBatis3Utils
を用いたdefaultメソッドを定義し、呼び元でテーブル名を指定せずにDeleteを可能にする。- 以下の通り引数を渡し、
MyBatis3Utils.deleteFrom
を呼び出す。- 第2引数: Supportクラス内のSqlTableを継承したクラスのstatic参照
Mapperを呼び出すクラス
Supportクラスをstatic importし、以下の通りCRUD操作を呼び出します。
public class FriendMapperCaller { @Autowired private FriendMapper friendMapper; public void execute() { // Create FriendEntity newFriend = FriendEntity.builder().name("Jon").build(); friendMapper.insert(newFriend); // Read friendMapper.selectOne(c -> c.where(id, isEqualTo(1))) // Update friendMapper.update(c -> c.set(name).equalTo("Bob").where(id, isEqualTo(1))); // Delete friendMapper.delete(c -> c.where(id, isEqualTo(1))); } }