Spring Data JDBCは、Spring Bootの依存関係です。
プロジェクトを立ち上げる際、Eclipseも用いると、
設定を進めていくうちに、
この画面で、SQLのリストの中にあり、チェックを入れると
使用することができます。
今回は、このSpring Data JPAの使用方法を
サンプルコードを用いてご紹介します。
なお、使用例についてはJUnitを用い、
使用したコードは、以下のGitHubへ載せています。
Spring Data JPAの使用方法は、
Spring Data JDBCの使い方と非常に似ています。
しかし、違いもあります。
個人的にはJPAの方が使いやすいと思ってます。
気になる方は、この記事をぜひ読み進めてみてください。
環境・定義
build.gradle
plugins {
id 'java'
id 'org.springframework.boot' version '3.2.0'
id 'io.spring.dependency-management' version '1.1.4'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
java {
sourceCompatibility = '17'
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.junit.jupiter:junit-jupiter:5.5.2'
}
tasks.named('test') {
useJUnitPlatform()
}
今回、DBはh2データベースを使用し、
GetterやSetterの生成には、lombokを用います。
application.properties
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:hogedb
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.hibernate.ddl-auto=validate
spring.sql.init.mode=always
spring.sql.init.schema-locations=classpath:schema.sql
spring.sql.init.data-locations=classpath:data.sql
spring.jpa.hibernate.ddl-autoの設定値については、以下に示します。
設定値 | 説明 | 備考 |
---|---|---|
none | DDLスクリプトを生成しない。スキーマの更新は手動。 | |
validate | データベースの更新、作成はしないが、検証をする。 | テーブルの型とEntityの型が違うなどのことがあるとコンパイルエラーとなる。 |
update | Entityに対応するテーブルがなければ作成。 | |
create | Entityに対応するテーブルが無けれ作成。あればデータを削除する。 | |
create-drop | createの挙動にプラスし、アプリケーション終了時にテーブルを削除する。 |
spring.sql.init.modeの設定値については、以下に示します。
設定値 | 説明 |
---|---|
always | src/main/resources配下のschema.sql, data.sqlを常に実行 |
embedded | H2、HSQLなどの組み込みデータベースの際に、src/main/resources配下のschema.sql, data.sqlを実行 |
never | SQLスクリプトを実行しない |
schema.sql
CREATE TABLE test_table
(
id INT NOT NULL AUTO_INCREMENT,
first_name VARCHAR(10) NOT NULL,
last_name VARCHAR(10) NOT NULL,
birth_day INT NULL,
PRIMARY KEY(id)
);
data.sql
INSERT INTO test_table(id,first_name, last_name, birth_day)
VALUES(1,'テスト', '太郎', 20240101);
INSERT INTO test_table(id,first_name, last_name, birth_day)
VALUES(2,'テスト', '二郎', 20240101);
INSERT INTO test_table(id,first_name, last_name, birth_day)
VALUES(3,'テスト', '三郎', 20240101);
INSERT INTO test_table(id,first_name, last_name, birth_day)
VALUES(4,'テスト', '花子', 20250101);
ファイル階層
- /
- src/
- main/
- java/
- com/example/demo/
- Entity/
- JPAEntity.java
- Repository/
- JPARepository.java
- Entity/
- com/example/demo/
- resources/
- application.properties
- shema.sql
- data.sql
- ……
- java/
- test/
- java/com/example/demo/Repository/
- JPARepositoryTest.java
- java/com/example/demo/Repository/
- main/
- build.gradle
- ……
- src/
サンプルコード
JPAEntity.java
package com.example.demo.Entity;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
/**
* JPAのテストエンティティ
* @author Takumi
*
*/
@Entity
@Getter
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "test_table")
public class JPAEntity {
@Id
private Integer id;
private String first_name;
private String last_name;
private Integer birth_day;
}
JPARepository.java
package com.example.demo.Repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.example.demo.Entity.JPAEntity;
public interface JPARepository extends JpaRepository<JPAEntity, Integer>{
}
JPARepositoryTest.java
package com.example.demo.Repository;
import static org.junit.jupiter.api.Assertions.*;
import java.util.List;
import java.util.Optional;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;
import com.example.demo.Entity.JPAEntity;
/**
* {@link JPARepository}のテスト
* @author Takumi
*
*/
@SpringBootTest
@Transactional
class JPARepositoryTest {
@Autowired
private JPARepository repository;
@Test
void findAllTest() {
List<JPAEntity> data = repository.findAll();
assertEquals(data.size(), 4);
assertEquals(data.get(0).getId(), 1);
assertEquals(data.get(0).getFirst_name().strip(), "テスト");
assertEquals(data.get(0).getLast_name().strip(), "太郎");
assertEquals(data.get(0).getBirth_day(), 20240101);
}
@Test
void findByIdTest() {
Optional<JPAEntity> dataOpt = repository.findById(1);
JPAEntity data = dataOpt.orElse(null);
assertEquals(data.getId(), 1);
assertEquals(data.getFirst_name().strip(), "テスト");
assertEquals(data.getLast_name().strip(), "太郎");
assertEquals(data.getBirth_day(), 20240101);
}
@Test
void saveTest() {
assertFalse(repository.existsById(9));
JPAEntity insertEntity = new JPAEntity(9,"テストくん", "テストちゃん", 20200202);
repository.save(insertEntity);
assertTrue(repository.existsById(9));
Optional<JPAEntity> insertDataOpt = repository.findById(9);
JPAEntity insertData = insertDataOpt.orElse(null);
assertEquals(insertData.getFirst_name().strip(), "テストくん");
assertEquals(insertData.getLast_name().strip(), "テストちゃん");
assertEquals(insertData.getBirth_day(), 20200202);
}
@Test
void deleteTest() {
assertTrue(repository.existsById(1));
repository.deleteById(1);
assertFalse(repository.existsById(1));
}
}
サンプルコード詳細説明
Spring Data JPAに関する部分のみご説明します。
JPAEntity.java
このクラスは、テーブルとのマッピングをします。
schema,sqlで定義しているテーブルとマッピングしています。
マッピングするにはまず、
@Entity、@Table、そして@Idが必要になります。
もちろん@Tableはテーブル名であり、
@Idはプライマリキーを指定します。
今回は、テーブルのカラム名と
Entityのフィールド名を一致させているため必要ありませんが、
もしフィールド名をカラム名と一致させていない場合は、
@Columnを用いてマッピングさせる必要があります。
余談ですが、
Spring Data JDBCとの違いは、
テーブル名に日本語でも英語の小文字でも用いることができる
ということです。
ただ、テーブル名にキャメルケース1を用いた場合は、
スネークケース2として判断されるようですので、注意が必要そうです。
JPARepository.java
このinterfaceは、jpaRepositoryを継承するのが肝です。
ここがSpring Data JPAの肝です。
第一引数にマッピングしたテーブルのEntityクラスを、
第二引数にEntityクラスの@Idをつけているフィールドの型、
つまり、テーブルのプライマリキーの型を渡して継承します。
このクラスを継承することで、
findAll、findById、delete、saveなどのメソッドを提供してくれ、
単純なSQL文であれば書く必要がなくなります。
JPARepositoryTest.java
テストケースごとに、Spring Data JPAに関連するところのみ
説明します。
findAllTest
findAllはテーブルにあるデータを全て取得するメソッドです。
@Test
void findAllTest() {
List<JPAEntity> data = repository.findAll();
assertEquals(data.size(), 4);
assertEquals(data.get(0).getId(), 1);
assertEquals(data.get(0).getFirst_name().strip(), "テスト");
assertEquals(data.get(0).getLast_name().strip(), "太郎");
assertEquals(data.get(0).getBirth_day(), 20240101);
}
3行目、
List<JPAEntity> data = repository.findAll();
で全てのデータを取得しています。
その後、
5〜9行目、
assertEquals(data.size(), 4);
assertEquals(data.get(0).getId(), 1);
assertEquals(data.get(0).getFirst_name().strip(), “テスト”);
assertEquals(data.get(0).getLast_name().strip(), “太郎”);
assertEquals(data.get(0).getBirth_day(), 20240101);
でデータが正しく取得できていることを確認しています。
(本来ならば、テーブルにある4データすべての確認をするべきですが、
ここでは省略します。)
findByIdTest
findByIdは引数に取得したいデータのプライマリキーの値を渡すことで、
データを取得するメソッドです。
@Test
void findByIdTest() {
Optional<JPAEntity> dataOpt = repository.findById(1);
JPAEntity data = dataOpt.orElse(null);
assertEquals(data.getId(), 1);
assertEquals(data.getFirst_name().strip(), "テスト");
assertEquals(data.getLast_name().strip(), "太郎");
assertEquals(data.getBirth_day(), 20240101);
}
3〜4行目、
Optional<JPAEntity> dataOpt = repository.findById(1);
JPAEntity data = dataOpt.orElse(null);
で、データを取得しています。
その後、
6〜9行目、
assertEquals(data.getId(), 1);
assertEquals(data.getFirst_name().strip(), “テスト”);
assertEquals(data.getLast_name().strip(), “太郎”);
assertEquals(data.getBirth_day(), 20240101);
でデータがきちんと取得できているかを
確認しています。
saveTest
saveメソッドは、引数に渡したマッピングしたEntityクラスのデータを
テーブルへインサート、もしくはテーブルをアップデートするメソッドです。
今回は、インサートの例です。
@Test
void saveTest() {
assertFalse(repository.existsById(9));
JPAEntity insertEntity = new JPAEntity(9,"テストくん", "テストちゃん", 20200202);
repository.save(insertEntity);
assertTrue(repository.existsById(9));
Optional<JPAEntity> insertDataOpt = repository.findById(9);
JPAEntity insertData = insertDataOpt.orElse(null);
assertEquals(insertData.getFirst_name().strip(), "テストくん");
assertEquals(insertData.getLast_name().strip(), "テストちゃん");
assertEquals(insertData.getBirth_day(), 20200202);
}
3行目、
assertFalse(repository.existsById(9));
で、Idが9のデータが存在しないことを確認しています。
その後、
5〜6行目、
JPAEntity insertEntity = new JPAEntity(9,”テストくん”, “テストちゃん”, 20200202);
repository.save(insertEntity);
で、Idが9のデータをインサートし、
8行目、
assertTrue(repository.existsById(9));
で、インサートされたのかを確認しています。
最後に、
10〜15行目、
Optional<JPAEntity> insertDataOpt = repository.findById(9);
JPAEntity insertData = insertDataOpt.orElse(null);
assertEquals(insertData.getFirst_name().strip(), “テストくん”);
assertEquals(insertData.getLast_name().strip(), “テストちゃん”);
assertEquals(insertData.getBirth_day(), 20200202);
で、データが正しくインサートされているのかを確認しています。
deleteTest
deleteByIdは、引数に消したいデータのプライマリキーの値を渡すことで、
そのデータを削除するメソッドです。
@Test
void deleteTest() {
assertTrue(repository.existsById(1));
repository.deleteById(1);
assertFalse(repository.existsById(1));
}
3行目、
assertTrue(repository.existsById(1));
でIdが1のデータが損じすることを確認しています。
その後、
5行目、
repository.deleteById(1);
で、Idが1のデータを削除し、
7行目、
assertFalse(repository.existsById(1));
で、Idが1のデータが存在しなくなったことを確認しています。
参考サイト