Spring Boot @RepositoryRestResource で CRUD の自動作成

Java
Javaバックエンド

この記事では @RepositoryRestResource を使った エンティティの CRUD 方法を説明します。

Spring だと @RestController で、簡単に API を作ることができますが、 @RepositoryRestResource を利用すると、それすらも実装することなく API を実現することができます。使い所は限定されるかもしれませんが、シンプルなテーブルに対する CRUD ができればよい、という場合にはものの数十分で作ることもできるのではないでしょうか。

その後の拡張性も高いとい思うと、とても便利ですね。

プロジェクト作成

spring initializr でベースを作ります。今回はH2 データベースを使います。H2はOSSのRDMBSで、インストールしなくても利用することができます。

  • Spring Boot 2.7.1
  • Spring Web
  • Spring Data JPA
  • Spring Rest Repositories
  • H2 Database
https://start.spring.io/ で今回のプロジェクト設定をダウロード
https://start.spring.io/ で今回のプロジェクト設定をダウロード

エンティティの作成

ここでは、API ではユーザをリソースとして扱います。ので、エンティティとなるクラスを作ります。H2 のデータベースでは user が予約後みたいなので、Person という名前にしています。ID に関しては、GenerationType.Auto を設定することで、自動インクリメントされます。

ID は自動生成、あとは名前はメールアドレスなどシンプルなモデルです。

// Person.java

package com.example.repositoryrestresourcesample;

import lombok.Data;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Data
@Entity
public class Person {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;
    private String name;
    private String email;
    private Integer age;
    private Boolean enabled;
}

Repository の作成

上の Person を扱う Repository を作ります。PagingAndSortingRepository を継承した PersonRepositoryです。型に Person を指定しています。

// PersonRepository.java

package com.example.repositoryrestresourcesample;

import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;

@RepositoryRestResource(collectionResourceRel = "users", path = "users")
public interface PersonRepository extends PagingAndSortingRepository<Person, Long> {
    // 中身は空でOK
}

Java のコードはこれで以上です。びっくりです。あとは、データベースの設定です。

テーブルの作成

なくても Sprint が上記のクラスから自動で作ってくれるかもしれませんが、Spring ではアプリの起動時に事前に用意していた SQL を自動で実行させることができます。そのためには、application.properties に設定を入れておきます。

spring.jpa.hibernate.ddl-auto と、HIBERNATE_SEQUENCE にしばらくハマりました。。が、以下のような設定をすることで、動かせるはずです。

// application.properties

// H2 を利用する
spring.datasource.driver-class-name=org.h2.Driver

// データソースの場所 mem を指定するとインメモリになります。
// jdbc:h2:~/userdb などとすると、ファイルになります。
spring.datasource.url=jdbc:h2:mem:userdb
spring.datasource.username=xxx
spring.datasource.password=xxx

// 起動時に SQL 実行をします。以下が実行されます。
//   /src/main/resources/schema.sql
//   /src/main/resources/data.sql
spring.datasource.initialization-mode=always
// これを書かないと実行されません
spring.jpa.hibernate.ddl-auto=none

// h2 のコンソールが開けるように
// Spring boot 起動後、以下にアクセスするとコンソールを開くことができます。
// http://localhost:8080/h2-console
spring.h2.console.enabled=true
-- schema.sql

CREATE TABLE IF NOT EXISTS person (
    id  VARCHAR(8)  PRIMARY KEY,
    name VARCHAR(255),
    email VARCHAR(255),
    age INTEGER,
    enabled BOOLEAN
 );

-- id が GenerationType.AUTO の場合、CREATE SEQUENCE が指定だと以下のエラーが発生します。
-- シーケンス "HIBERNATE_SEQUENCE" が見つかりません
-- Sequence "HIBERNATE_SEQUENCE" not found; SQL statement:
-- Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessResourceUsageException: could not prepare statement; SQL [call next value for hibernate_sequence]; nested exception is org.hibernate.exception.SQLGrammarException: could not prepare statement] with root cause
CREATE SEQUENCE HIBERNATE_SEQUENCE START WITH 100 INCREMENT BY 1;
// data.sql

INSERT INTO person (id, name, email, age, enabled)VALUES
    ('10000000', 'test1', 'test1@example.com', 30, true),
    ('10000001', 'test2', 'test2@example.com', 16, true),
    ('10000002', 'test3', 'test3@example.com', 49, true);

アプリケーションの実行

アプリケーションを実行すると、schema.sql によりテーブルが作成され、data.sql によりデータが投入されます。そして API が実行できるようになっています。

GET

ブラウザで「http://localhost:8080/users」へアクセスすると、以下のように取得できているのが確認できます。

ブラウザから http://localhost:8080/users を行った結果
ブラウザから http://localhost:8080/users を行った結果

POST での登録

こちらは curl から実行しています。追加することももちろん可能です。

% curl -X POST http://localhost:8080/users -H "Content-Type:application/json" -d "{\"name\" : \"hoge\", \"age\" : 99, \"email\" : \"999@example.com\", \"enabled\" : true}" 
{
  "name" : "hoge",
  "email" : "999@example.com",
  "age" : 99,
  "enabled" : true,
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/users/101"
    },
    "person" : {
      "href" : "http://localhost:8080/users/101"
    }
  }
}

その他のパラメータ

Repository で PagingAndSortingRepository を継承したのでイメージつくかもしれませんが、ページングやソートといったことも可能です。また、PersonRepository の中身が空でしたが、こちらを実装することで、search のようなことも可能です。

多くの場合、特に業務アプリケーションなどでは単一のテーブルに対するシンプルな操作で済むことはまずありませんが、簡易なマスタ(表示項目マスタなど)であれば、何も実装することなく API が実現できるのは便利かなと思います。

コメント