Spring Bootで作成したAPIのDBアクセスを「MyBatis」を利用してみたいと思います。
MyBatisとは
MyBatisとは、JavaのDBアクセスを簡単にするOSSでO/Rマッピングツールです。
これを利用することでこんなメリットがあります。
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文や改行して見やすくすることとかもできるようです。