Skip to content

MyBatis Dynamic SQL使い方

MyBatis Dynamic SQLを使って動的にSQLを発行します。

前提

下記DDLで作成されるテーブルにアクセスします:

-- テーブル作成
CREATE TABLE users(
    user_id int PRIMARY KEY AUTO_INCREMENT
    , user_name VARCHAR(20) NOT NULL
    , age int NOT NULL
    , address TEXT
);

-- テストデータ
INSERT INTO users(
    user_name
    , age
    , address
) VALUES (
    'nob'
    , 13
    , 'This is a test address'
);

事前準備

  • application.propertiesに接続情報を記載します:
#MariaDBのドライバ設定
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
#接続用URL
spring.datasource.url=jdbc:mariadb://localhost/eadb
#ユーザ名
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.19</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>4.0.0</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 nob.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 = "nob.example.easyapp.domain.mapper") // mapperパッケージ配下をスキャン
public class MyBatisConfig {

    @Bean
    SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);

        return sessionFactory.getObject();
    }
}

実装例

  • テーブル定義に対応するエンティティクラスを作成します:
package nob.example.easyapp.domain.entity;

import lombok.Value;

/**
 * usersテーブルのentityクラスです。
 *
 * @author nob
 */
@Value
public class Users {

    /** ユーザID */
    private Integer userId;

    /** ユーザ名 */
    private String userName;

    /** 年齢 */
    private Integer age;

    /** 住所 */
    private String address;
}
  • mapperパッケージに、sqlSupportクラスおよびmapperクラスを作成します:
package nob.example.easyapp.domain.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> address = users.address;

    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> address = column("address", JDBCType.VARCHAR);

        public Users() {
            super("users");
        }
    }
}
package nob.example.easyapp.domain.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 nob.example.easyapp.domain.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 = "address", property = "address"),
    })
    List<Users> select(SelectStatementProvider selectStatement);

    /**
     * insert
     *
     * @param insertStatement
     * @return
     */
    @InsertProvider(type = SqlProviderAdapter.class, method = "insert")
    int insert(InsertStatementProvider<Users> insertStatement);
}
  • repositoryクラスを作成します:
package nob.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 nob.example.easyapp.domain.entity.Users;
import nob.example.easyapp.domain.mapper.UsersMapper;
import nob.example.easyapp.domain.mapper.UsersDynamicSqlSupport;

/**
 * usersテーブルのrepositoryクラスです。
 *
 * @author nob
 */
@Repository
public class UsersRepository {

    @Autowired
    private UsersMapper usersMapper;

    /**
     * ユーザを検索します。
     *
     * @param condition
     * @return 検索結果
     */
    public List<Users> selectByCondition(Integer userId, String userName) {

        SelectStatementProvider selectStatement = SqlBuilder.select(UsersDynamicSqlSupport.users.allColumns())
                .from(UsersDynamicSqlSupport.users)
                .where(UsersDynamicSqlSupport.users.userName, SqlBuilder.isEqualToWhenPresent(userName))
                .and(UsersDynamicSqlSupport.users.userId, SqlBuilder.isEqualToWhenPresent(userId))
                .build()
                .render(RenderingStrategies.MYBATIS3);

        return usersMapper.select(selectStatement);
    }

    /**
     * ユーザを登録します。
     *
     * @param users
     */
    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.address).toProperty("address")
                .build()
                .render(RenderingStrategies.MYBATIS3);

        usersMapper.insert(insertStatement);
    }
}

テスト例

H2DBを使ってテストします。

  • h2dbの依存関係を追記します:
        <!-- h2db導入 -->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- Jpa Test導入 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-data-jpa-test</artifactId>
            <scope>compile</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 nob.example.easyapp.repository;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;

import java.util.List;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.TestPropertySource;

import nob.example.easyapp.domain.entity.Users;

/**
 * UsersRepositoryのテストクラスです。
 *
 * @author nob
 */
@SpringBootTest
@ActiveProfiles("test") // application-test.properties読み込み
@TestPropertySource(properties = {
        "spring.sql.init.schema-locations=classpath:/users/schema.sql", // テーブル作成SQLのパス
        "spring.sql.init.data-locations=classpath:/users/data.sql" // データ投入SQLのパス
})
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class UsersRepositoryTest {

    @Autowired
    private UsersRepository usersRepository;

    /**
     * テスト
     */
    @Test
    void test_findByUserId() {

        try {
            List<Users> u = usersRepository.selectByCondition(1, "test_nob");
            assertEquals(1, u.size());
            assertEquals(1, u.get(0).getUserId());
            assertEquals("test_nob", u.get(0).getUserName());
            assertEquals(13, u.get(0).getAge());
            assertEquals("test address01", u.get(0).getAddress());
        } catch (Exception e) {
            e.printStackTrace();
            fail();
        }
    }
}
  • src/test/resources/users/schema.sqlおよびsrc/test/resources/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
    , address TEXT
);
-- data.sql
INSERT INTO users(
    user_name
    , age
    , address
) VALUES (
    'test_nob'
    , 13
    , 'test address01'
);