MyBatis Dynamic SQL 使い方
MyBatis Dynamic SQLを使って動的に SQL を発行します。
cf. https://mybatis.org/mybatis-dynamic-sql/docs/introduction.html
前提
下記 DDL で作成されるテーブルにアクセスします:
-- テーブル作成
CREATE TABLE users(
user_id int PRIMARY KEY AUTO_INCREMENT
, user_name VARCHAR(20) NOT NULL
, age int NOT NULL
, remarks TEXT
);
-- テストデータ
INSERT INTO users(
user_name
, age
, remarks
) VALUES (
'nob'
, 13
, 'This is a test data'
);
事前準備
application.propertiesに接続情報を記載します:
#MariaDBのドライバ設定
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
#接続用URL
spring.datasource.url=jdbc:mariadb://localhost/snaildb
#ユーザ名
spring.datasource.username=root
#パスワード
spring.datasource.password=password
- 依存関係を
pom.xmlに記載します:
<!-- https://mvnrepository.com/artifact/org.mybatis.dynamic-sql/mybatis-dynamic-sql -->
<dependency>
<groupId>org.mybatis.dynamic-sql</groupId>
<artifactId>mybatis-dynamic-sql</artifactId>
<version>1.5.2</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.16</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
</dependency>
- MyBatis のコンフィグクラスを作成します:
package com.example.easyapp.config;
import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* MyBatis関連のコンフィグクラスです。
*
* @author nob
*/
@Configuration
@MapperScan(basePackages = "com.example.easyapp.mapper") // mapperパッケージ配下をスキャン
public class MyBatisConfig {
// MyBatisの設定
@Bean
SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
return sessionFactory.getObject();
}
}
実装例
- テーブル定義に対応するエンティティクラスを作成します:
package com.example.easyapp.entity;
import lombok.Value;
/**
* usersテーブルのentityクラスです。
*
* @author nob
*/
@Value
public class Users {
/** ユーザID */
private Integer userId;
/** ユーザ名 */
private String userName;
/** 年齢 */
private Integer age;
/** 備考 */
private String remarks;
}
- mapper パッケージに、
sqlSupportクラスおよびmapperクラスを作成します:
package com.example.easyapp.mapper;
import java.sql.JDBCType;
import org.mybatis.dynamic.sql.SqlColumn;
import org.mybatis.dynamic.sql.SqlTable;
/**
* usersテーブルのsqlSupportクラスです。
*
* @author nob
*/
public class UsersDynamicSqlSupport {
public static final Users users = new Users();
public static final SqlColumn<Integer> userId = users.userId;
public static final SqlColumn<String> userName = users.userName;
public static final SqlColumn<String> age = users.age;
public static final SqlColumn<String> remarks = users.remarks;
public static final class Users extends SqlTable {
public final SqlColumn<Integer> userId = column("user_id", JDBCType.INTEGER);
public final SqlColumn<String> userName = column("user_name", JDBCType.VARCHAR);
public final SqlColumn<String> age = column("age", JDBCType.VARCHAR);
public final SqlColumn<String> remarks = column("remarks", JDBCType.VARCHAR);
public Users() {
super("users");
}
}
}
package com.example.easyapp.mapper;
import java.util.List;
import org.apache.ibatis.annotations.SelectProvider;
import org.apache.ibatis.annotations.InsertProvider;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.mybatis.dynamic.sql.insert.render.InsertStatementProvider;
import org.mybatis.dynamic.sql.select.render.SelectStatementProvider;
import org.mybatis.dynamic.sql.util.SqlProviderAdapter;
import com.example.easyapp.entity.Users;
/**
* usersテーブルのmapperクラスです。
*
* @author nob
*/
@Mapper
public interface UsersMapper {
/**
* select
*
* @param selectStatement
* @return
*/
@SelectProvider(type = SqlProviderAdapter.class, method = "select")
@Results(id = "usersResult", value = {
@Result(column = "user_id", property = "userId"),
@Result(column = "user_name", property = "userName"),
@Result(column = "age", property = "age"),
@Result(column = "remarks", property = "remarks"),
})
List<Users> select(SelectStatementProvider selectStatement);
/**
* insert
*
* @param insertStatement
* @return
*/
@InsertProvider(type = SqlProviderAdapter.class, method = "insert")
int insert(InsertStatementProvider<Users> insertStatement);
}
- repository クラスを作成します:
package com.example.easyapp.repository;
import java.util.List;
import org.mybatis.dynamic.sql.SqlBuilder;
import org.mybatis.dynamic.sql.insert.render.InsertStatementProvider;
import org.mybatis.dynamic.sql.render.RenderingStrategies;
import org.mybatis.dynamic.sql.select.render.SelectStatementProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.example.easyapp.entity.Users;
import com.example.easyapp.mapper.UsersMapper;
import com.example.easyapp.mapper.UsersDynamicSqlSupport;
import com.example.easyapp.model.condition.UsersSelectCondition;
/**
* usersテーブルのrepositoryクラスです。
*
* @author nob
*/
@Repository
public class UsersRepository {
@Autowired
private UsersMapper usersMapper;
/**
* ユーザを検索します。
*
* @param condition 検索条件
* @return ヒットしたユーザのリスト
*/
public List<Users> selectByCondition(UsersSelectCondition condition) {
SelectStatementProvider selectStatement = SqlBuilder.select(UsersDynamicSqlSupport.users.allColumns())
.from(UsersDynamicSqlSupport.users)
.where(UsersDynamicSqlSupport.users.userName, SqlBuilder.isEqualToWhenPresent(condition.getUserName()))
.and(UsersDynamicSqlSupport.users.userId, SqlBuilder.isEqualToWhenPresent(condition.getUserId()))
.build()
.render(RenderingStrategies.MYBATIS3);
return usersMapper.select(selectStatement);
}
/**
* ユーザを登録します。
*
* @param users エンティティ
* @return
*/
public void insert(Users users) {
InsertStatementProvider<Users> insertStatement = SqlBuilder.insert(users)
.into(UsersDynamicSqlSupport.users)
.map(UsersDynamicSqlSupport.userId).toProperty("userId")
.map(UsersDynamicSqlSupport.userName).toProperty("userName")
.map(UsersDynamicSqlSupport.age).toProperty("age")
.map(UsersDynamicSqlSupport.remarks).toProperty("remarks")
.build()
.render(RenderingStrategies.MYBATIS3);
usersMapper.insert(insertStatement);
}
}
UsersSelectConditionについては下記モデルクラスを作成しています:
package com.example.easyapp.model.condition;
import lombok.Value;
/**
* usersテーブル検索時の条件を格納するモデルです。
*
* @author nob
*/
@Value
public class UsersSelectCondition {
/** ユーザ名 */
private String userName;
}
テスト例
H2DB を使ってテストします。
- h2db の依存関係を追記します:
<!-- h2db導入 -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
src/test/resources/application-test.propertiesを下記内容で作成します:
# エンティティクラスからスキーマを自動生成しない
spring.jpa.hibernate.ddl-auto=none
# h2db接続設定
spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
- 下記要領でテストクラスを作成します:
package com.example.easyapp.repository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.TestPropertySource;
/**
* UsersRepositoryのテストクラスです。
*
* @author nob
*/
@SpringBootTest
@ActiveProfiles("test") // application-test.properties読み込み
@TestPropertySource(properties = {
"spring.sql.init.schema-locations=classpath:/testdata/users/schema.sql", // テーブル作成SQLのパス
"spring.sql.init.data-locations=classpath:/testdata/users/data.sql" // データ投入SQLのパス
})
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class UsersRepositoryTest {
@Autowired
private UsersRepository usersRepository;
/**
* テスト
*/
@Test
void test() {
// テストケース
}
}
src/test/resources/testdata/users/schema.sqlおよびsrc/test/resources/testdata/users/data.sqlは下記要領で作成します:
-- schema.sql
DROP TABLE IF EXISTS users;
CREATE TABLE IF NOT EXISTS users(
user_id int PRIMARY KEY AUTO_INCREMENT
, user_name VARCHAR(20) NOT NULL
, age int NOT NULL
, remarks TEXT
);
-- data.sql
INSERT INTO users(
user_name
, age
, remarks
) VALUES (
'nob'
, 13
, 'This is a test data'
);