flywayのサンプル

#目的
Frywayを利用したDBの利用法の最小構成を整理する。

#環境
Java8
IDE STS
マイグレーションツール　Flyway
O/Rマッパー Spring Data JPA
DB H2Database

#手順

##プロジェクト
![スクリーンショット 2016-01-02 16.11.17.png](https://qiita-image-store.s3.amazonaws.com/0/57455/af1529fb-2a9e-0ff6-fd48-106f7f23d169.png)


##pom
DB周りに関わる依存性として、以下の内容を追記する。


```xml:pom.xml
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jdbc</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>

		<dependency>
			<groupId>com.h2database</groupId>
			<artifactId>h2</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.flywaydb</groupId>
			<artifactId>flyway-core</artifactId>
		</dependency>
```

##画面 
テンプレートエンジン
Thymeleafを利用

```HTML:list.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>top page</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" type="text/css" href="../../static/css/style.css" th:href="@{/css/style.css}" />
<link rel="stylesheet" type="text/css" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css" th:href="@{/webjars/bootstrap/3.2.0/css/bootstrap-theme.min.css}" />
</head>
<body>
<div class="container">
	<h1>テスト</h1>
	<!-- 登録を行う  -->
	<div class="col-sm-12">
	<!-- th:objectでは、フォームクラス名の頭小文字を指定すると、フォームオブジェクトとHTMLフィールドをバインドでき、
	このクラスのフィールド名で　*{フィールド名}で値を呼び出せる -->
		<form th:action="@{/testdb/create}" th:object="${testForm}"
			method="post">
			<fieldset>
			<legend>登録</legend>
			<div class="form-group" th:classappend="${#fields.hasErrors('col01')}? 'has-error has-feedback'">
				<label for="col01" class="col-sm-2 control-label">col01</label>
				<div class="col-sm-10">
					<input type="text" id="col01" name="col01" th:field="*{col01}" class="form-control" value="col01" />
					<span th:if="${#fields.hasErrors('col01')}" th:errors="*{col01}" class="help-block">error!</span>
				</div>
			</div>
			<div class="form-group" th:classappend="${#fields.hasErrors('col02')}? 'has-error has-feedback'">
				<label for="col02" class="col-sm-2 control-label">col02</label>
				<div class="col-sm-10">
					<input type="text" id="col02" name="col02" th:field="*{col02}" class="form-control" value="col02" />
					<span th:if="${#fields.hasErrors('col02')}" th:errors="*{col02}" class="help-block">error!</span>
					
				</div>
			</div>
			<div class="form-group">
				<div class="col-sm-offset-2 col-sm-10">
					<button type="submit" class="btn btn-primary">作成</button>
				</div>
			</div>
			</fieldset>
		</form>
		
		<ht/>
		
		<!-- DB登録済みの内容をリストに書き出す  -->
		
		<table class="table table-striped table-bordered table-condensed">
			<tr>
				<th>ID</th>
				<th>姓</th>
				<th>名</th>
				<th colspan="2">編集</th>
			</tr>
			<!-- ${オブジェクト}は、コントローラ側で(model.addAttribute("testdata", view);)で指定した名前に紐付いたデータがとれる -->
			<tr th:each="test : ${testdata}">
			<!-- eachで、testに写し、一つずつ取り出せる -->
				<td th:text="${test.id}">100</td>
				<td th:text="${test.col01}">001</td>
				<td th:text="${test.col02}">002</td>
				<td>
					<form th:action="@{/testdb/delete}" method="post">
						<input class="btn btn-danger" type="submit" value="削除" />
						<!-- 繰り返された一つのtestオブジェクトのidをvalueにわたし、コントローラに渡す -->
						<input type="hidden" name="id" th:value="${test.id}" />
					</form>
				</td>
			</tr>
		</table>
	</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js" th:src="@{/webjars/jquery/1.11.1/jquery.min.js}">
</script>
<script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js" th:src="@{/webjars/bootstrap/3.2.0/js/bootstrap.min.js}"></script>

</body>
</html>

```


##データベース
データベースの設定
H２データベースの作成位置を指定する。

```yaml:property.yml
spring:
  datasource:
    driverClassName: org.h2.Driver
    url: jdbc:h2:file:/tmp/test_tbl #
    username: sa
    password:
  jpa:
    hibernate.ddl-auto: validate #
  thymeleaf.cache: false
```

##コントローラ

```java:TestController
package com.example.web;

import java.util.List;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import com.example.domain.Test;
import com.example.service.TestService;

@Controller
@RequestMapping("testdb") //ルートとなるパスを指定している
public class TestController {

	@Autowired
	TestService testservice;

	@ModelAttribute
	TestForm setUpForm() {
		//登録時データ受け渡し用のフォーム
		return new TestForm();
	}

	@RequestMapping(method = RequestMethod.GET)
	String list(Model model) {
		// サービスから取得した全件データを画面側へ送る
		List<Test> view = testservice.findAll();
		model.addAttribute("testdata", view);
		return "views/list";
	}

　　　　// testdb/create　へPOSTメソッドで呼び出した場合ここが呼び出される
	@RequestMapping(value = "create", method = RequestMethod.POST) 
	String create(@Validated TestForm form, BindingResult result, Model model) {
		// @Validatedアノテーションで、フォームに指定されていた制約をチェックできる。結果はresultに入る
		if (result.hasErrors()) {
			return list(model);
		}
		// 登録データの受け渡しフォームを作成し
		Test testdata = new Test();
		// サービスから登録する
		BeanUtils.copyProperties(form, testdata);//左から右に値をコピー
		testservice.create(testdata);
		//ルートへリダイレクトする
		return "redirect:/testdb";
	}	
	@RequestMapping(value = "delete", method = RequestMethod.POST)
	String delete(@RequestParam Integer id){
		testservice.delete(id);
		return "redirect:/testdb";
	}
}
```

###フォームクラス
画面からデータ登録時に受け渡しに利用するフォームクラス

```java:TestForm.java
package com.example.web;

import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

import lombok.Data;

@Data
public class TestForm {
	@NotNull
	@Size(min=1, max=10)
	private String col01;
	@NotNull
	@Size(min=1, max=10)
	private String col02;
}
```

##サービス

```java:TestService.java
package com.example.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.example.domain.Test;
import com.example.repository.TestRepository;

@Service
@Transactional
public class TestService {
	@Autowired
	TestRepository TestRepository;
	
	public Test save(Test Test){
		return TestRepository.save(Test);
	}
	public List<Test> findAll(){
		return TestRepository.findAll();
	}
	public Test findOne(Integer id){
		return TestRepository.findOne(id);
	}
	public Test create(Test test){
		return TestRepository.save(test);
	}
	public Test update(Test test){
		return TestRepository.save(test);
	}
	public void delete(Integer id){
		TestRepository.delete(id);
	}
}
```
※ここでAutowierdによるインジェクションに失敗するエラーがでてうまくいかなかった。
　原因はこのサービスが利用しているrepositoryクラスでエラーが起きていた。
　エラーは、repositoryが利用していたTestクラス（ドメインのクラス）が問題があった
　ドメインのクラスが、@Entityになっていなかったため（javax.persistance.Entityじゃなかった）、
　DBとフィールドがバンドできていなかった。

##DBの操作
###リポジトリ
　SpringJPA の用意するインターフェースを継承するだけで、基本的なCLUD操作が使えるようになる

```java:TestRepository
package com.example.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.example.domain.Test;

@Repository
@Transactional
public interface TestRepository extends JpaRepository<Test, Integer>{
}

```

###ドメインクラス
@Entityにより、DBのテーブルとフィールドがバインドされている。
@DataアノテーションはLambokによるもので、アクセッサを自動生成している。
その下でコンストラクタも自動生成されている

```java:Test
package com.example.domain;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Entity // javax.persistance.Entityの@Entityのアノテーションを使う、hibernateの@Entityではない
@Table(name = "test_tbl")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Test {
	@Id
	@GeneratedValue
	private Integer id;
	@Column(nullable = false)
	private String col01;
	@Column(nullable = false)
	private String col02;

}

```

###マイグレーション
![スクリーンショット 2016-01-02 16.26.55.png](https://qiita-image-store.s3.amazonaws.com/0/57455/1ac145e7-bb7e-0297-9d48-23efa92a2b9f.png)

以下に
db/migration
ディレクトリを用意し、そこにマイグレーションファイルを配置する

V１（連番）__(アンダーバー二つ)任意のファイル名.sql
のルールでファイルを用意する

スクリプトを追加する場合は、V以降の番号を繰り上げたファイルを同じディレクトリに配置する

ここでは、テーブル定義を行う最初のDDLを実行している

```SQL:V1__create-table.sql
CREATE TABLE test_tbl (id INT PRIMARY KEY AUTO_INCREMENT, col01 VARCHAR(30), col02 VARCHAR(30));
```
・バージョン番号後のアンダーバーは二つ必要
・一度流したスクリプトは変更しない、変えたければALTERTABLEとかを流す
・追加のスクリプトは、バージョンを繰り上げないと流れない、エラーとなる

ブラウザより
localhost:8080/testdb/
へリクエスト

以下のようにルートの画面へ遷移が行われる

![スクリーンショット 2016-01-02 22.27.11.png](https://qiita-image-store.s3.amazonaws.com/0/57455/dd7a3667-6951-a2b5-03d5-422ebf152102.png)