mybatisを動かしてみた話
CRUD最小構成に適用してみる
- お決まりのPlayerのCRUDサンプルを作ってみる
共通部分
- mybatisと関連の少ない部分
- templateは省略
controller
- 典型的なCRUDのエンドポイント
PlayerController.java
@Controller
@RequestMapping("/players")
public class PlayerController {
@Autowired
private PlayerService playerService;
@GetMapping
public String index(Model model) {
model.addAttribute("players", playerService.findAll());
return "players/index";
}
@GetMapping("new")
public String newPlayer() {
return "players/new";
}
@GetMapping("{id}/edit")
public String edit(@PathVariable Long id, Model model) {
model.addAttribute("player", playerService.findOne(id));
return "players/edit";
}
@GetMapping("{id}")
public String show(@PathVariable Long id, Model model) {
model.addAttribute("player", playerService.findOne(id));
return "players/show";
}
@PostMapping
public String create(@ModelAttribute Player player) {
playerService.save(player);
return "redirect:/players";
}
@PutMapping("{id}")
public String update(@PathVariable Long id, @ModelAttribute Player player) {
player.setId(id);
playerService.update(player);
return "redirect:/players";
}
@DeleteMapping("{id}")
public String destroy(@PathVariable Long id) {
playerService.delete(id);
return "redirect:/players";
}
}
service
- ここからmybatisの処理を呼んでいる
- PlayerMapperはこの後作るmybatisの処理
PlayerService.java
@Service
public class PlayerService {
@Autowired
private PlayerMapper playerMapper;
@Transactional
public List<Player> findAll() {
return playerMapper.findAll();
}
@Transactional
public Player findOne(Long id) {
return playerMapper.findOne(id);
}
@Transactional
public void save(Player player) {
playerMapper.save(player);
}
@Transactional
public void update(Player player) {
playerMapper.update(player);
}
@Transactional
public void delete(Long id) {
playerMapper.delete(id);
}
}
domain
- playerテーブルのentity
Player.java
public class Player {
private Long id;
private String name;
private String team;
private String position;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTeam() {
return team;
}
public void setTeam(String team) {
this.team = team;
}
public String getPosition() {
return position;
}
public void setPosition(String position) {
this.position = position;
}
}
ベーシックなパターン
依存関係
- 他のパターンでも共通
- pom.xmlにmybatisの依存関係を追加
pom.xml
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>
mapper
-
@Mapper
のついたinterfaceを作成する - アノテーションでSQLを記述する
PlayerMapper.java
@Mapper
public interface PlayerMapper {
@Select("select * from player")
List<Player> findAll();
@Select("select * from player where id = #{id}")
Player findOne(Long id);
@Insert("insert into player (name, team, position) values (#{name}, #{team}, #{position})")
@Options(useGeneratedKeys = true)
void save(Player player);
@Update("update player set name = #{name}, team = #{team}, position = #{position} where id = #{id}")
void update(Player player);
@Delete("delete from player where id = #{id}")
void delete(Long id);
}
- これだけで動く
- この状態の全量はこちら
- もしくはこちら
git clone -b mybatis1 https://github.com/ozaki25/SpringMybatisSample.git
SQLをxmlに書くパターン
- 上の書き方でもいいが、もっと複雑なことをしたかったり、SQLが長くなった時複数行で書けないし、JavaとSQLは分けて書きたいと思うかもしれない
- そんな人のためのxmlにSQLを書くパターン
mapper
- SQLが消えた
PlayerMapper.java
@Mapper
public interface PlayerMapper {
List<Player> findAll();
Player findOne(Long id);
void save(Player player);
void update(Player player);
void delete(Long id);
}
xml
- SQLがこっちに移った
PlayerMapper.xml
<?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="com.example.mapper.PlayerMapper">
<select id="findAll" resultType="com.example.domain.Player">
select * from player
</select>
<select id="findOne" resultType="com.example.domain.Player">
select * from player where id = #{id}
</select>
<insert id="save" useGeneratedKeys="true" keyProperty="id">
insert into player (name, team, position) values (#{name}, #{team}, #{position})
</insert>
<update id="update">
update player set name = #{name}, team = #{team}, position = #{position} where id = #{id}
</update>
<delete id="delete">
delete from player where id = #{id}
</delete>
</mapper>
-
同じフォルダ内に置いておけば設定ファイルに何も書かなくて動く
-
実行結果はベーシックなパターンと全く同じ
-
ただxmlにSQLを切り出しただけ
-
この状態の全量はこちら
-
もしくはこちら
-
git clone -b mybatis2 https://github.com/ozaki25/SpringMybatisSample.git
- or
git checkout -b mybatis2 mybatis2
-
-
しかし、これだけだとJPAで書いたほうが簡単なんじゃない?となってしまう
JPAで同じものを作ったパターン
- 変更点は以下の3ファイル
domain
- JPA用のアノテーションを追加
Player.java
@Entity
public class Player {
@Id
@GeneratedValue
private Long id;
private String name;
private String team;
private String position;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTeam() {
return team;
}
public void setTeam(String team) {
this.team = team;
}
public String getPosition() {
return position;
}
public void setPosition(String position) {
this.position = position;
}
}
service
- 各メソッドからの呼び出し先をrepositoryに変更
PlayerService.java
@Service
public class PlayerService {
@Autowired
private PlayerRepository playerRepository;
@Transactional
public List<Player> findAll() {
return playerRepository.findAll();
}
@Transactional
public Player findOne(Long id) {
return playerRepository.findOne(id);
}
@Transactional
public void save(Player player) {
playerRepository.save(player);
}
@Transactional
public void update(Player player) {
playerRepository.save(player);
}
@Transactional
public void delete(Long id) {
playerRepository.delete(id);
}
}
repository
- maybatisで書いているMapperの代わりになるようなもの
- 典型的なものはextendsしている先ですでに用意されているので空っぽでも動く
PlayerRepository.java
@Repository
public interface PlayerRepository extends JpaRepository<Player, Long> {
}
-
JPA版のコードはこちら
-
cloneはこちら
-
git clone -b jpa https://github.com/ozaki25/SpringMybatisSample.git
- or
git checkout -b jpa jpa
-
-
JPAとmybatisの差分としてはこんな感じ
mybatisが活きるところ
- 肝心なところの作成が間に合わなかった
- 以下のページにあるようにSQLにロジックを埋め込めちゃうところがウリっぽい