概要
STSを使ってSpring Data JPAを試したときの備忘録を残しておきます。
主に自分用。
Spring Data JPAとはSpringのO/Rマッパー、
モデル層のフレームワークです。
紹介の前に言葉の説明
O/Rマッパー
DBのテーブルやビューのレコード1行を
Javaのインスタンス1個として扱い、
テーブルとインスタンスを
関連付けるモジュールです。
後ほどもう少し詳しく書いていきます。
※厳密には違いますがイメージは
大体合ってると思います。
モデル層
MVC(Model, View, Controller)のModelです。
DBそのものや、DBへのCRUD(Create, Read, Update, Delete)、
アプリケーションのロジック部分のことです。
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のセットアップは自分の備忘録を参考にしました。
プロジェクトを作る
セットアップした後は
パッケージエクスプローラ上で右クリック、
新規でプロジェクト作成します。
種類はSpring Boot > Spring starter projectです。
DBの準備
敢えてOracleにしました。
理由はSpring Boot + Oracleという組み合わせ、
後々実務にありそうだと思ったからです。
あと「敢えて」というのは
MySQLとかSTSにDatabase Driverが
同梱されているDBもある一方、
Oracleは含まれていない、ということです。
接続先のデータベースはOracle Database 11g Express Editionで
こちらを参考に作りました。
ブラウザから管理者権限でDBユーザー作ったり便利です。
ただ、このままではSpring Bootプロジェクトから
Oracle Databaseに接続できないので、
OracleからDriverをダウンロードします。
Maven Repository
で見当たらなかったので、
直接ライブラリをプロジェクトに追加しました。
あかんやつです。
本来、MavenとかGradleとかで
プロジェクト管理するのが推奨ですが、
無かったんですよね。
#実務でもこうなるような気がします。
作ったDBに以下のテーブル、レコードを追加。
CREATE TABLE USER (
USER_ID VARCHAR2(20),
USER_NAME VARCHAR2(20),
AGE NUMBER(3,0),
CONSTRAINT PK_USER PRIMARY KEY(USER_ID)
);
ここでO/Rマッパーについて少し補足しておきます。
上記のテーブルに対して、次のようなクラスを用意します。
UserInfo.java テーブルUSER_INFOに対応するクラス
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、
といった具合です。
さて、作ったプロジェクトはこちらです。
作ったクラス
次に各クラスを紹介します。
ちなみにこの記事ではSpringApplication.run
しているクラスは
デフォルトから編集していないので紹介を割愛します。
UserInfoのRepository
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を書きます。
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テストケース これで動作確認しています。
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 設定ファイル
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実行ログを抜粋しました。
Hibernate: select * from USER_INFO where USER_ID = ?
userId = user001, userName = userName001, age = 40
最後に
最後まで読んでいただきありがとうございました。