【Spring】mybatis-springを利用してDBアクセス

Spring Bootで作成したAPIのDBアクセスを「MyBatis」を利用してみたいと思います。

MyBatisとは

MyBatisとは、JavaのDBアクセスを簡単にするOSSでO/Rマッピングツールです。
これを利用することでこんなメリットがあります。

メリット

・SQLをコード内に記載する必要がなくなる(外部ファイルに記載)
・マッピングが容易に可能

API作成

SpringでAPIを作成します。簡単なCRUD全てが実行できるようにします。
※MyBatisがメインになるので主要クラスのみのせています。

package sample.api.sample;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
@RequestMapping("api/sample")
public class SampleRestController {

	
	private final SampleService service;
	
	@Autowired
	public SampleRestController(SampleService service) {
		this.service = service;
	}
	
	@GetMapping
	public List<SampleResource> getList(SampleResource resource) {
		return service.findList(resource);
	}
	
	@GetMapping("{id}")
	public SampleResource getSample(@PathVariable("id") int id) {
		return service.findById(id);
	}
	
	@PostMapping
	public int insert(@RequestBody @Validated SampleResource resource) {
		return service.insert(resource);
	}
	
	@PutMapping
	public int update(@RequestBody @Validated SampleResource resource) {
		return service.update(resource);
	}
	
	@DeleteMapping("{id}")
	public int delete(@PathVariable("id") int id) {
		return service.delete(id);
	}
	
}
package sample.api.sample;
 
import java.util.List;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import sample.api.mybatis.SampleMapper;
import sample.api.sample.error.NotFoundException;

 
@Service
@Transactional
public class SampleService {
 
    private final SampleMapper mapper;
 
    @Autowired
    public SampleService(SampleMapper mapper) {
        this.mapper = mapper;
    }
 
    public SampleResource findById(int id) {
    	SampleResource result = mapper.findById(id);
    	if(result == null) {
    		String errorMessage = "対象データが存在しません";
    		throw new NotFoundException(errorMessage);
    	}
    	return result;
	}

    public List<SampleResource> findList(SampleResource form) {
        return mapper.findAll();
    }

    public int insert(SampleResource form) {
		return mapper.insert(form);
	}

    public int update(SampleResource form) {
		return mapper.update(form);
	}

    public int delete(int id) {
		return mapper.delete(id);
	}
}
package sample.api.sample;

import java.io.Serializable;

public class SampleResource implements Serializable {
	
	private static final long serialVersionUID = 1L;

	private int id;
	private String cd;
	private String name;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getCd() {
		return cd;
	}
	public void setCd(String cd) {
		this.cd = cd;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}

}

mybatis-spring利用

概要

2つ方法を紹介します。
①mybatis-spring-boot-starterを利用
②上記を利用しない方法

①を利用することで自動で依存関係等を環境構築する上で簡単です。
※開発統合環境はSTSを利用

SQLをxmlファイルに作成し、それを利用します。

①mybatis-spring-boot-starter

mybatisのスターターを利用します。「MyBatis Framework」をインストールします。

Mapperインターフェースを作成します。
xmlで定義することも可能みたいですが、「@Mapper」を利用します。

package sample.api.mybatis;

import java.util.List;

import org.apache.ibatis.annotations.Mapper;

import sample.api.sample.SampleResource;

@Mapper
public interface SampleMapper {
	List<SampleResource> findAll();
	
	SampleResource findById(int id);
	
	int insert(SampleResource resource);
	
	int update(SampleResource resource);
	
	int delete(int id);
}

SQLを記載するxmlファイルを作成します。
xmlはresouces配下に作成します。

「DOCTYPE」は決まり文句のようです。
「namespace」は上記で作成したインターフェースの場所を記載します。
「id」とインターフェースのメソッド名は一致させましょう。
「resultType」「parameterType」は文字通りパラメータと戻り値の型となります。
戻り値にBeanクラスを指定すると自動でマッピングしてくれます。
この辺の詳細は以下公式サイトを参考にしてください。
https://mybatis.org/mybatis-3/ja/sqlmap-xml.html

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="sample.api.mybatis.SampleMapper">
   <select id="findAll" resultType="sample.api.sample.SampleResource">
      select * from sample
   </select>
   <select id="findById" parameterType="integer" resultType="sample.api.sample.SampleResource">
      select * from sample where id = #{id}
   </select>
   <insert id="insert" parameterType="sample.api.sample.SampleResource">
      insert into sample(cd, name) values(#{cd}, #{name})
   </insert>
   <update id="update" parameterType="sample.api.sample.SampleResource">
      update sample set cd = #{cd}, name = #{name} where id = #{id}
   </update>
   <delete id="delete" parameterType="integer">
      delete from sample where id = #{id}
   </delete>
</mapper>

Mapperインターフェースとxmlファイルの作成箇所には注意が必要です。
現状こんな感じにしています。
(パッケージは本来「jp」等作成すべきですが省略しています)

この2ファイルが同じパスであれば特殊な記述は必要ありません。
※同じパス
例)main/java/sample/api/mybatis/SampleMapper.java
main/resources/sample/api/mybatis/SampleMapper.xml

ただ、画像のように異なる場合はどこにxmlファイルがあるかを「yaml」もしくは「properties」に指定する必要があります。
下記の「mybatis.mapper-locations」です

spring.datasource.url=jdbc:postgresql://localhost:5432/diary
spring.datasource.username=appuser
spring.datasource.password=appuser
mybatis.mapper-locations=classpath*:sample/mybatis/*.xml

②starterを利用しない

依存関係のあるものを自分で解決する必要があります。
今回はgradleで管理しているので「build.gradle」に「mybatis-spring」「mybatis」を追記します。
※バージョンはその都度確認してください。
https://mvnrepository.com/artifact/org.mybatis/mybatis-spring/2.0.7

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
	implementation 'org.springframework.boot:spring-boot-starter-web'
//	implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.2.2'
	runtimeOnly 'org.postgresql:postgresql'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	implementation 'org.mybatis:mybatis-spring:2.0.7'
	implementation 'org.mybatis:mybatis:3.5.9'
}

追記後、gradleをリフレッシュします。

依存関係だけなく、Config.javaを作成する必要があります。
※xmlで記述する方法もあります。以下を参考に。
https://mybatis.org/spring/ja/getting-started.html
https://mybatis.org/spring/ja/mappers.html

もう少し良い記載方法はあると思いますが。。。
DBとの接続設定を記載しないといけないようです。
①で記載した「mybatis.mapper-locations」もDBとのやりとり必要な情報は下記に記載するため、「setMapperLocations」で指定しています。

package sample.api.config;

import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.postgresql.ds.PGSimpleDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;

@Configuration
@PropertySource("classpath:application.properties")
@MapperScan("sample.api.mybatis")
public class AppConfig {
	@Value("${spring.datasource.username}")
	private String username;
	@Value("${spring.datasource.password}")
	private String password;
	@Value("${spring.datasource.url}")
	private String url;
	@Bean
	public SqlSessionFactory sqlSessionFactory() throws Exception {
		SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
		sessionFactory.setDataSource(dataSource());
		ResourcePatternResolver resolver =
				ResourcePatternUtils.getResourcePatternResolver(new DefaultResourceLoader());
		sessionFactory.setMapperLocations(resolver.getResource("classpath:sample/mybatis/SampleMapper.xml"));
		return sessionFactory.getObject();
	}
	
	@Bean
	public DataSource dataSource() {
		PGSimpleDataSource p = new PGSimpleDataSource();
		p.setUrl(url);
		p.setUser(username);
		p.setPassword(password);
		return p;
	}
}

その他のxmlやMapperインターフェースは①で記載と同じです。

まとめ

このような感じでSQLをコーティングと切り離すことができます。
JPAのようにSQLを記載しないで作成されるものもありますが、SQLをがっつり書きたいときは良いのではないでしょうか。他にもxml内でif文や改行して見やすくすることとかもできるようです。

タイトルとURLをコピーしました