STSでSpring Data JPAを試したときの備忘録 

## 概要
STSを使ってSpring Data JPAを試したときの備忘録を残しておきます。
主に自分用。
Spring Data JPAとはSpringのO/Rマッパー、
モデル層のフレームワークです。

## 紹介の前に言葉の説明
### O/Rマッパー
DBのテーブルやビューのレコード1行を
Javaのインスタンス1個として扱い、
テーブルとインスタンスを
関連付けるモジュールです。
後ほどもう少し詳しく書いていきます。
※厳密には違いますがイメージは
　大体合ってると思います。

### モデル層
MVC(Model, View, Controller)のModelです。
DBそのものや、DBへのCRUD(**C**reate, **R**ead, **U**pdate, **D**elete)、
アプリケーションのロジック部分のことです。

## SpringでJPAを推す理由
Spring Data JPAの強みは
クラスやメソッドの命名規則などの
お約束を守ればインターフェースを作るだけで
CRUDできます。便利です。

ただし、SQLを書く必要があるとき、
SQLの代わりにJPQLという、
SQLにJavaが混じったような**きつめの方言**
のSQLを書くパターンが多いようです。

つまり、インターフェースだけで実装できる
シンプルなCRUDでは要件を満たしきれない可能性があります。
カスタマイズするケースもあるでしょう。
でも、JPQLは書きたくない。
SQLで書きたいですよね？**書けるんです。**

そんな私のような方、この記事では
直接SQLを書いて実装していきます。
この記事ではCRUDのうちのR(Read)をサンプルに
使い方を紹介していきます。

## STSでSpring Data JPAのセットアップ
折角なので、STSを使って、
Spring Bootプロジェクトで作ってみます。
Webアプリケーションからの検証ではなく、
JUnitからの検証にしたいと思います。
ちなみに検証PCのOSはWindows 7です。

### その前にSTSのセットアップ
STSのセットアップは[自分の備忘録](https://qiita.com/harayoshi/items/fca60e68087640b26292)を参考にしました。

### プロジェクトを作る
セットアップした後は
パッケージエクスプローラ上で右クリック、
新規でプロジェクト作成します。

![パッケージエクスプローラ上で右クリック](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533981/791457ae-642a-ff21-ada3-b4401a50d38a.png)


種類はSpring Boot > Spring starter projectです。
![Spring Boot > Spring starter project](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533981/52622b7f-d4b7-0ce0-afe9-869d8942a519.png)


![Spring Bootプロジェクト設定](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533981/5591e498-53c6-ae40-c313-9dc954fa4023.png)


### DBの準備
敢えてOracleにしました。
理由はSpring Boot + Oracleという組み合わせ、
後々実務にありそうだと思ったからです。

あと「敢えて」というのは
MySQLとかSTSにDatabase Driverが
同梱されているDBもある一方、
Oracleは含まれていない、ということです。

接続先のデータベースは[Oracle Database 11g Express Edition](http://www.oracle.com/technetwork/jp/database/database-technologies/express-edition/downloads/index.html)で
[こちら](https://qiita.com/GohanOmori/items/0b58a656174c767e239e)を参考に作りました。
ブラウザから管理者権限でDBユーザー作ったり便利です。

ただ、このままではSpring Bootプロジェクトから
Oracle Databaseに接続できないので、
[OracleからDriverをダウンロード](http://www.oracle.com/technetwork/database/enterprise-edition/jdbc-112010-090769.html)します。
[Maven Repository](https://mvnrepository.com)
で見当たらなかったので、
直接ライブラリをプロジェクトに追加しました。
あかんやつです。
本来、MavenとかGradleとかで
プロジェクト管理するのが推奨ですが、
無かったんですよね。
＃実務でもこうなるような気がします。

作ったDBに以下のテーブル、レコードを追加。

```sql
CREATE TABLE USER (
  USER_ID VARCHAR2(20), 
  USER_NAME VARCHAR2(20), 
  AGE NUMBER(3,0), 
  CONSTRAINT PK_USER PRIMARY KEY(USER_ID)
);
```

![作成したテーブル、レコード](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533981/f5268105-998b-76e0-9f07-05fe889fbf9d.png)

ここでO/Rマッパーについて少し補足しておきます。
上記のテーブルに対して、次のようなクラスを用意します。

**UserInfo.java** テーブルUSER_INFOに対応するクラス

```java
package jp.co.illmatics.app.samples.entity;

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class UserInfo {

	@Id
	public String userId;

	public String userName;
	
	public Integer age;

	/**
	 * 取得したレコードを文字列化
	 */
	public String toString() {
		return "userId = " + userId + 
		       ", userName = " + userName + 
		       ", age = " + age;
	}

}
```

テーブル名、カラム名は
UpperSnakeCaseからLowerCamelCaseに変えることで
クラス名、フィールド名と紐付きます。

この場合、
テーブル名USER_INFOがクラス名UserInfo、
カラムUSER_IDがフィールド名userId、
といった具合です。

さて、作ったプロジェクトはこちらです。

![プロジェクト全体](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533981/727102dc-4144-8de6-fa34-a15f42cf634e.png)


### 作ったクラス
次に各クラスを紹介します。
ちなみにこの記事では```SpringApplication.run```しているクラスは
デフォルトから編集していないので紹介を割愛します。

**UserInfoのRepository**

```Java
package jp.co.illmatics.app.samples.repository;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import jp.co.illmatics.app.samples.entity.UserInfo;

public interface UserInfoRepository 
		extends JpaRepository<UserInfo, String> {
	public Optional<UserInfo> findByUserId(String userId);
}
```

JpaRepositoryをextendsするのがお約束、
genericsにエンティティとIDのクラスを指定します。

**DBアクセスするクラスの実体** ここでSQLを書きます。

```Java
package jp.co.illmatics.app.samples.repository.impl;

import java.util.List;
import java.util.Optional;

import javax.persistence.EntityManager;
import javax.persistence.Query;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Component;

import jp.co.illmatics.app.samples.entity.UserInfo;
import jp.co.illmatics.app.samples.repository.UserInfoRepository;

@Component
public class UserInfoRepositoryImpl implements UserInfoRepository {

	@Autowired
	EntityManager manager;

	@SuppressWarnings("unchecked")
	@Override
	public Optional<UserInfo> findByUserId(String userId) {
		String sql = "select * from USER_INFO "
					+ "where USER_ID = :userId";
		Query query = manager.createNativeQuery(sql, UserInfo.class);
		query.setParameter("userId", userId);
		UserInfo rawResult = (UserInfo) query.getSingleResult();
		Optional<UserInfo> result = Optional.ofNullable(rawResult);
		return result;
	}
       // abbreviated
}
```
※使わないメソッドはオーバーライドだけして
　実装していないため、省略しています。

**JUnitテストケース** これで動作確認しています。

```Java
package jp.co.illmatics.app.samples.repository.impl;

import static org.junit.Assert.*;

import java.util.Optional;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import jp.co.illmatics.app.samples.entity.UserInfo;

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserInfoRepositoryImplTest {

	private String userId = "user001";
	
	@Autowired
	UserInfoRepositoryImpl userInfoRepositoryImpl;
	
	@Test
	public void testFindByUserId() {
		Optional<UserInfo> result = 
			userInfoRepositoryImpl.findByUserId(userId);
		assertTrue(result.isPresent());
		System.out.println(result.get().toString());
	}

}
```
ポイントはクラスにつけたアノテーション。
```@RunWith(SpringRunner.class)``` でSpringを使ったクラスのテストができます。
SpringRunnerとはSpringJUnit4ClassRunnerのことです。
あとは```@SpringBootTest```でプロジェクト内のクラスにDIを使えます。

**application.properties** 設定ファイル

```properties
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.url=jdbc:oracle:thin:@localhost:1521:XE
spring.datasource.username=xxxxxx
spring.datasource.password=xxxxxx
spring.jpa.database-platform=org.hibernate.dialect.Oracle10gDialect
spring.jpa.show-sql=true
```

※Dialectの指定が10gになっていますが、試してみたら11gと同じでした。

JUnit実行ログを抜粋しました。

```text
Hibernate: select * from USER_INFO where USER_ID = ?
userId = user001, userName = userName001, age = 40
```

# 最後に
最後まで読んでいただきありがとうございました。