【Spring Data JPA】基本操作とクエリ実装
導入
Spring Data JPA を使用するために、以下を追加します。 ドライバーは使用する DB に合わせて追加してください。
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
}
application.yml(.properties)に、データベースの接続設定をします。 以下は MySQL の設定例です。
spring:
datasource:
url: jdbc:mysql://localhost:3306/testdb
username: hoge
password: fugafuga
driver-class-name: com.mysql.cj.jdbc.Driver
基本操作
Entity の作成
Spring Data JPA では、テーブルに対応する Entity クラスを作成する必要があります。
Entity クラスには、以下のように@Entity
を付与します。
@Entity
@Table(name = 'user')
public class User {
@Id
@Column(name = "id")
private String id;
@Column(name = "name")
private String name;
@Column(name = "age")
private int age;
//セッター・ゲッターは省略
}
@Table
は、テーブル名とクラス名が同じであれば省略できます。
@Column
も同様に、テーブルのフィールド名とクラスのフィールド名が同じであれば省略できます。
キー項目には@Id
を付与します。
Repository の作成
DB 操作用の Repository インターフェースを以下のように作成します。
@Repository
public interface UserRepository extends JpaRepository<User, String>{
}
@Repository
を付与し、JpaRepository<>
を継承します。
JpaRepository<>
のジェネリクスには、対象となる Entity クラスと、キー項目の型を指定します。
結果の取得
作成した Repository を Inject して、CRUD に対応するメソッドを使用します。
※ 下例では、わかりやすさのために Controller で使用していますが、実際は Service で使用してください。
@RestController
@RequestMapping("/api/user")
public class UserController {
@Autowired
private UserRepository repository;
@GetMapping
public List<User> getAll() {
//全件取得
return repository.findAll();
}
@GetMapping("{id}")
public User getById(@PathVariable("id") String id) {
//IDで検索
return repository.findById(id).orElseThrow();
}
@PostMapping
public void save(@RequestMapping User user) {
//更新
repository.save(user);
}
@DeleteMapping("{id}")
public User delete(@PathVariable("id") String id) {
//削除
repository.deleteById(id);
}
}
以上が Spring Data JPA の基本的な使用方法になります。
クエリ実装
基本クエリ
Repository では、上記で使用したような CRUD に関するメソッドがいくつかデフォルトで生成されます。 以下はそのメソッドの一部になります。
メソッド | 引数 | 戻り値 | 説明 |
---|---|---|---|
findAll() | List<T> | すべてのレコードを取得 | |
findById() | キー | Optional<T> | キーに該当するレコードを取得 |
getById() | キー | T | キーに該当するレコードを取得 |
count() | long | レコード数を取得 | |
existsById() | キー | boolean | キーに該当するレコードが存在するかを取得 |
save() | T | T | キーが存在する場合は更新、存在しない場合は登録 |
saveAll() | List<T> | List<T> | Entity の数だけsave() を実行 |
deleteById() | キー | キーに該当するレコードを削除 | |
deleteAllById() | List<キー> | キーの数だけdeleteById() を実行 |
カスタムクエリ
例えばname
を条件として検索し、レコードを取得したいとします。
これを実現するためには、Repository に以下のメソッドを追加します。
@Repository
public interface UserRepository extends JpaRepository<User, String>{
List<User> findByName(String name);
}
あとは必要な箇所でこのメソッドを実行するだけです。
List<User> users = repository.findByName("太郎");
Spring Data JPA では、Repository にルールに従ってメソッドを定義することで、 自身でクエリを生成をせずに、目的の結果を得ることができます。 上記の場合は、以下のクエリが実行されたことになります。
select * from user
where name = ?
ルールというのは、メソッドの命名です。 決まったキーワードを組み合わせることで目的を満たすクエリが自動で生成されます。
まず初めに目的を設定します。
上記はレコードの取得のためにfindBy
というキーワードを使用しました。
その他以下のキーワードがあります。
キーワード | メソッドサンプル | 説明 |
---|---|---|
findBy | findByName(String name) | 条件に一致するレコードの取得 |
existsBy | existsByName(String name) | 条件に一致するレコードが存在するか(true /false ) |
countBy | countByName(String name) | 条件に一致するレコード数 |
deleteBy | deleteByName(String name) | 条件に一致するレコードの削除 |
次に条件を設定します。
上記では、findByName
とすることで「name
と一致する」という条件を設定しています。
等価条件以外は、以下のキーワードを用いて設定できます。
複数フィールドを条件とする場合は、メソッドで指定したフィールド順序とパラメーターの順序を同じにします。
キーワード | メソッドサンプル | クエリ |
---|---|---|
Not | findByNameNot(String name) | ... where name <> ?1 |
And | findByNameAndAge(String name, int age) | ... where name = ?1 and age = ?2 |
Or | findByNameOrAge(String name, int age) | ... where name = ?1 or age = ?2 |
LessThan | findByAgeLessThan(int age) | ... where age < ?1 |
LessThanEqual | findByAgeLessThanEqual(int age) | ... where age <= ?1 |
GreaterThan | findByAgeGreaterThan(int age) | ... where age > ?1 |
GreaterThanEqual | findByAgeGreaterThanEqual(int age) | ... where age <= ?1 |
Between | findByAgeBetween(int min, int max) | ... where age between ?1 and ?2 |
IsNull | findByNameIsNull() | ... where name is null |
IsNotNull | findByNameIsNotNull() | ... where name is not null |
In | findByNameIn(String... name) | ... where name in ?1 |
NotIn | findByNameNotIn(String... name) | ... where name not in ?1 |
Like | findByNameLike(String name) | ... where name like ?1 |
NotLike | findByNameNotLike(String name) | ... wehre name not like ?1 |
StartingWith | findByNameStartingWith(String name) | ... where name like ?1% |
EndingWith | findByNameEndingWith(String name) | ... where name like %?1 |
Containing | findByNameContaining(String name) | ... where name like %?1% |
True | findByActiveTrue() | ... where active = true |
False | findByActiveFalse() | ... where active = false |
IgnoreCase | findByIdIgnoreCase(String id) | ... where UPPER(id) = UPPER(?1) |
最後に取得する結果に対して手を加えます。 以下のキーワードを使用することで、並び替えや取得レコード数を指定することができます。
キーワード | メソッドサンプル | 説明 |
---|---|---|
OrderBy | findByNameOrderByAgeDesc(String name) | レコード順序の指定 |
Top , First | findTop3ByName(String name) | 指定した数だけ上位のレコードを取得(Top 、First は同義) |
Distinct | findDistinctByName(String name) | 同一結果のレコードの集約 |
JPQL
JPQL とは
JPQL は Java Persistence Query Language の略で、JPA で使用できるクエリです。 後述しますが、JPQL を使って独自のクエリを作成することができます。
基本的な文法はネイティブなクエリと同じですが、テーブルではなく Entity をベースに作成する点が異なります。
例えば、name
を条件にレコードを取得するクエリは以下のようになります。
select u from User u where u.name = ?1
FROM 句には、Entity 名と識別変数(AS 句)が必要になります。 SELECT 句や WHERE 句などでは、識別変数を使用してフィールドを指定します。
パラメーターは、「?1
, ?2
, ?3
, ...」と順に設定します。d
または:name
のように、名前付きパラメーターも使用できます。
また JPQL では以下の句、演算子、関数が使用できます。
句
- SELECT, UPDATE, DELETE
- WHERE, CASE
- ORDER BY
- GROUP BY, HAVING
- JOIN(INNER, OUTER)
- 副問い合わせ(WHERE, HAVING 内でのみ)
演算子
分類 | 演算子 |
---|---|
四則演算子 | + , - , * , / |
論理演算子 | AND , OR , NOT |
関係演算子 | = , <> , < , > , <= , >= |
特殊演算子 | BETWEEN , IN , LIKE , IS NULL , IN EMPTY |
関数
分類 | 関数 | 説明 |
---|---|---|
集約関数 | COUNT | レコード数 |
MAX | 最大値 | |
MIN | 最小値 | |
AVG | 平均値 | |
SUM | 合計値 | |
文字列関数 | CONCAT | 文字列連結 |
SUBSTRING | 文字列抽出 | |
TRIM | 空白削除 | |
LOWER | 小文字変換 | |
UPPER | 大文字変換 | |
LENGTH | 文字列の長さ | |
LOCATE | 文字列検索位置 | |
算術関数 | ABS | 絶対値 |
SQRT | 平方根 | |
MOD | 剰余 | |
SIZE | コレクションの要素数 | |
INDEX | 要素の位置 | |
日時関数 | CURRENT_DATE | 現在日 |
CURRENT_TIME | 現在時刻 | |
CURRENT_TIMESTAMP | タイムスタンプ |
@NamedQuery
JPQL を用いたクエリの作成方法の 1 つとして、@NamedQuery
があります。
これは、Entity に付与するアノテーションで、プロパティにname
とquery
を指定します。
@Entity
@NamedQuery(name = "myFindByAge", query = "select u from User u where u.age = ?1")
public class User {
//...
}
使用する場合は、Repository にname
で指定した名前のメソッドを定義します。
List<User> myFindByAge(int age);
メソッドパラメーターの順序と?1
の順序を合わせる必要があります。
@Query
もう 1 つの実装方法として、@Query
があります。
@NamedQuery
は Entity に付与しましたが、@Query
は Repository のメソッドに付与します。
@Query("select u from User u where u.age = ?1")
List<User> myFindByAge(int age);
メソッドパラメーターに@Param
を付与することで、名前付きパラメーターを使用することができます。
@Query("select u from User u where u.age = :age")
List<User> myFindByAge(@Param("age") int age);
また以下のようにnativeQuery
プロパティを設定することで、JPQL ではなくネイティブなクエリを指定することができます。
@Query(value = "select * from user where age = ?1", nativeQuery = true)
List<User> myFindByAge(int age);
@Modifying
@Modifying
は、@Query
で更新形(UPDATE、DELETE)のクエリを指定した場合に必要となります。
またこの場合、Repository に@Transactional
を付与する必要もあります。
@Transactional
@Repository
public interface UserRepository extends JpaRepository<User, String> {
@Modifying
@Query("update User u set age = :age where id = :id")
void setAge(@Param("age") int age, @Param("id") String id);
}