このガイドでは、ハイパーメディアベースRESTful (英語) フロントエンドを介してリレーショナル JPA データにアクセスするアプリケーションを作成するプロセスについて説明します。

構築するもの

Spring Data REST を使用して、データベースに保存された Person オブジェクトを作成および取得できる Spring アプリケーションを構築します。Spring Data REST は、Spring HATEOASSpring Data JPA の機能を使用し、自動的に組み合わせます。

Spring Data REST は、バックエンドデータストアとして Spring Data Neo4jSpring Data Gemfire、および Spring Data MongoDB もサポートしていますが、これらはこのガイドの一部ではありません。

必要なもの

このガイドを完了する方法

ほとんどの Spring 入門ガイドと同様に、最初から始めて各ステップを完了するか、すでに慣れている場合は基本的なセットアップステップをバイパスできます。いずれにしても、最終的に動作するコードになります。

最初から始めるには、Spring Initializr から開始に進みます。

基本スキップするには、次の手順を実行します。

完了したときは、gs-accessing-data-rest/complete のコードに対して結果を確認できます。

Spring Initializr から開始

すべての Spring アプリケーションでは、Spring Initializr (英語) から始める必要があります。Initializr は、アプリケーションに必要なすべての依存関係をすばやく取り込む方法を提供し、多くの設定を行います。この例では、Rest リポジトリ、Spring Data JPA、および H2 の依存関係が必要です。

次のリストは、Maven を選択したときに作成される pom.xml ファイルを示しています。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.3.2.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>accessing-data-rest</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>accessing-data-rest</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-rest</artifactId>
		</dependency>

		<dependency>
			<groupId>com.h2database</groupId>
			<artifactId>h2</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

次のリストは、Gradle を選択したときに作成される build.gradle ファイルを示しています。

plugins {
	id 'org.springframework.boot' version '2.3.2.RELEASE'
	id 'io.spring.dependency-management' version '1.0.8.RELEASE'
	id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'org.springframework.boot:spring-boot-starter-data-rest'
	runtimeOnly 'com.h2database:h2'
	testImplementation('org.springframework.boot:spring-boot-starter-test') {
		exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
	}
}

test {
	useJUnitPlatform()
}

ドメインオブジェクトを作成する

次のリスト(src/main/java/com/example/accessingdatarest/Person.java 内)が示すように、新しいドメインオブジェクトを作成して人を表示します。

package com.example.accessingdatarest;

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

@Entity
public class Person {

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private long id;

  private String firstName;
  private String lastName;

  public String getFirstName() {
    return firstName;
  }

  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }

  public String getLastName() {
    return lastName;
  }

  public void setLastName(String lastName) {
    this.lastName = lastName;
  }
}

Person オブジェクトには、名と姓があります。(また、自動生成されるように構成された ID オブジェクトもあるため、それに対処する必要はありません。)

個人リポジトリを作成する

次に、次のリスト(src/main/java/com/example/accessingdatarest/PersonRepository.java 内)が示すように、単純なリポジトリを作成する必要があります。

package com.example.accessingdatarest;

import java.util.List;

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

@RepositoryRestResource(collectionResourceRel = "people", path = "people")
public interface PersonRepository extends PagingAndSortingRepository<Person, Long> {

  List<Person> findByLastName(@Param("name") String name);

}

このリポジトリは、Person オブジェクトに関連するさまざまな操作を実行できるインターフェースです。Spring Data Commons で定義されている PagingAndSortingRepository(Javadoc) インターフェースを継承することにより、これらの操作を取得します。

実行時に、Spring Data REST はこのインターフェースの実装を自動的に作成します。次に、@RepositoryRestResource (英語) アノテーションを使用して Spring MVC に /people で RESTful エンドポイントを作成するよう指示します。

@RepositoryRestResource は、リポジトリのエクスポートには必要ありません。/persons のデフォルト値の代わりに /people を使用するなど、エクスポートの詳細を変更するためにのみ使用されます。

ここでは、lastName に基づいて Person オブジェクトのリストを取得するカスタムクエリも定義しました。このガイドの後半で呼び出す方法を確認できます。

@SpringBootApplication は、次のすべてを追加する便利なアノテーションです。

  • @Configuration: アプリケーションコンテキストの Bean 定義のソースとしてクラスにタグを付けます。

  • @EnableAutoConfiguration: クラスパス設定、他の Bean、およびさまざまなプロパティ設定に基づいて Bean の追加を開始するよう Spring Boot に指示します。例: spring-webmvc がクラスパスにある場合、このアノテーションはアプリケーションに Web アプリケーションとしてフラグを立て、DispatcherServlet のセットアップなどの主要な動作をアクティブにします。

  • @ComponentScan: Spring に、com/example パッケージ内の他のコンポーネント、構成、およびサービスを探して、コントローラーを検出させるように指示します。

main() メソッドは、Spring Boot の SpringApplication.run() メソッドを使用してアプリケーションを起動します。XML が 1 行もないことに気付きましたか? web.xml ファイルもありません。この Web アプリケーションは 100% 純粋な Java であり、接続機能やインフラストラクチャの構成に対処する必要はありませんでした。

Spring Boot は Spring Data JPA を自動的に起動して、PersonRepository の具体的な実装を作成し、JPA を使用してバックエンドインメモリデータベースと通信するように構成します。

Spring Data REST は、Spring MVC の上に構築されます。Spring MVC コントローラー、JSON コンバーター、およびその他の Bean のコレクションを作成して、RESTful フロントエンドを提供します。これらのコンポーネントは、Spring Data JPA バックエンドにリンクします。Spring Boot を使用すると、これはすべて自動構成されます。Spring Data REST の RepositoryRestMvcConfiguration を見て、その仕組みを調査したい場合。

実行可能 JAR を構築する

コマンドラインから Gradle または Maven を使用してアプリケーションを実行できます。必要なすべての依存関係、クラス、およびリソースを含む単一の実行可能 JAR ファイルを構築して実行することもできます。実行可能な jar を構築すると、開発ライフサイクル全体、さまざまな環境などで、アプリケーションとしてサービスを簡単に提供、バージョン管理、およびデプロイできます。

Gradle を使用する場合、./gradlew bootRun を使用してアプリケーションを実行できます。または、次のように、./gradlew build を使用して JAR ファイルをビルドしてから、JAR ファイルを実行できます。

java -jar build/libs/gs-accessing-data-rest-0.1.0.jar

Maven を使用する場合、./mvnw spring-boot:run を使用してアプリケーションを実行できます。または、次のように、./mvnw clean package で JAR ファイルをビルドしてから、JAR ファイルを実行できます。

java -jar target/gs-accessing-data-rest-0.1.0.jar
ここで説明する手順は、実行可能な JAR を作成します。クラシック WAR ファイルを作成することもできます。

ロギング出力が表示されます。サービスは数秒以内に起動して実行されるはずです。

アプリケーションをテストする

アプリケーションが実行されたため、テストできます。任意の REST クライアントを使用できます。以下の例では、*nix ツール curl を使用しています。

最初に、トップレベルのサービスを見たいです。次の例は、その方法を示しています。

$ curl http://localhost:8080
{
  "_links" : {
    "people" : {
      "href" : "http://localhost:8080/people{?page,size,sort}",
      "templated" : true
    }
  }
}

上記の例は、このサーバーが提供するものを最初に垣間見せます。http://localhost:8080/people には people リンクがあります。?page?size や ?sort などのオプションがあります。

Spring Data REST は、JSON 出力に HAL フォーマット (英語) を使用します。柔軟性があり、提供されるデータに隣接するリンクを提供する便利な方法を提供します。

次の例は、人物レコードを表示する方法を示しています(現時点ではなし)。

$ curl http://localhost:8080/people
{
  "_embedded" : {
    "people" : []
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/people{?page,size,sort}",
      "templated" : true
    },
    "search" : {
      "href" : "http://localhost:8080/people/search"
    }
  },
  "page" : {
    "size" : 20,
    "totalElements" : 0,
    "totalPages" : 0,
    "number" : 0
  }
}

現在、要素はないため、ページはありません。新しい Person を作成する時間です!次のリストは、その方法を示しています。

$ curl -i -H "Content-Type:application/json" -d '{"firstName": "Frodo", "lastName": "Baggins"}' http://localhost:8080/people
HTTP/1.1 201 Created
Server: Apache-Coyote/1.1
Location: http://localhost:8080/people/1
Content-Length: 0
Date: Wed, 26 Feb 2014 20:26:55 GMT
  • -i: ヘッダーを含むレスポンスメッセージを確認できるようにします。新しく作成された Person の URI が表示されます。

  • -H "Content-Type:application/json": コンテンツタイプを設定して、ペイロードが JSON オブジェクトを含むことをアプリケーションが認識できるようにします。

  • -d '{"firstName": "Frodo", "lastName": "Baggins"}': データが送信されています。

  • Windows を使用している場合、上記のコマンドは WSL (英語) で機能します。WSL をインストールできない場合は、一重引用符を二重引用符に置き換え、既存の二重引用符をエスケープする必要がある場合があります(例: -d "{\"firstName\": \"Frodo\", \"lastName\": \"Baggins\"}")。

POST 操作へのレスポンスに Location ヘッダーが含まれていることに注意してください。これには、新しく作成されたリソースの URI が含まれます。Spring Data REST には 2 つのメソッド(RepositoryRestConfiguration.setReturnBodyOnCreate(…) および setReturnBodyOnUpdate(…))もあり、作成したリソースの表現をすぐに返すようにフレームワークを構成するために使用できます。RepositoryRestConfiguration.setReturnBodyForPutAndPost(…) は、作成および更新操作の表現レスポンスを有効にするショートカットメソッドです。

次の例に示すように、すべての人を照会できます。

$ curl http://localhost:8080/people
{
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/people{?page,size,sort}",
      "templated" : true
    },
    "search" : {
      "href" : "http://localhost:8080/people/search"
    }
  },
  "_embedded" : {
    "people" : [ {
      "firstName" : "Frodo",
      "lastName" : "Baggins",
      "_links" : {
        "self" : {
          "href" : "http://localhost:8080/people/1"
        }
      }
    } ]
  },
  "page" : {
    "size" : 20,
    "totalElements" : 1,
    "totalPages" : 1,
    "number" : 0
  }
}

people オブジェクトには、Frodo を含むリストが含まれています。self リンクが含まれていることに注意してください。Spring Data REST はまた、Evo インフレクター (英語) を使用して、グループ化のためにエンティティの名前を複数形にします。

次のように、個々のレコードを直接照会できます。

$ curl http://localhost:8080/people/1
{
  "firstName" : "Frodo",
  "lastName" : "Baggins",
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/people/1"
    }
  }
}
これは、純粋に Web ベースのように見える場合があります。ただし、バックグラウンドでは、H2 リレーショナルデータベースがあります。本番環境では、おそらく PostgreSQL などの実際のものを使用します。
このガイドでは、ドメインオブジェクトは 1 つだけです。ドメインオブジェクトが互いに関連しているより複雑なシステムでは、Spring Data REST は追加のリンクをレンダリングして、接続されたレコードへのナビゲートを支援します。

次の例に示すように、すべてのカスタムクエリを見つけることができます。

$ curl http://localhost:8080/people/search
{
  "_links" : {
    "findByLastName" : {
      "href" : "http://localhost:8080/people/search/findByLastName{?name}",
      "templated" : true
    }
  }
}

HTTP クエリパラメーター name を含む、クエリの URL を確認できます。これは、インターフェースに埋め込まれた @Param("name") アノテーションと一致することに注意してください。

次の例は、findByLastName クエリの使用方法を示しています。

$ curl http://localhost:8080/people/search/findByLastName?name=Baggins
{
  "_embedded" : {
    "persons" : [ {
      "firstName" : "Frodo",
      "lastName" : "Baggins",
      "_links" : {
        "self" : {
          "href" : "http://localhost:8080/people/1"
        }
      }
    } ]
  }
}

コードで List<Person> を返すように定義しているため、すべての結果が返されます。Person のみを返すように定義していた場合、Person オブジェクトの 1 つを選択して返します。これは予測できないため、複数のエントリを返す可能性のあるクエリに対してはそうしたくないでしょう。

PUTPATCH および DELETE REST 呼び出しを発行して、既存のレコードを(それぞれ)置換、更新、または削除することもできます。次の例では、PUT 呼び出しを使用しています。

$ curl -X PUT -H "Content-Type:application/json" -d '{"firstName": "Bilbo", "lastName": "Baggins"}' http://localhost:8080/people/1
$ curl http://localhost:8080/people/1
{
  "firstName" : "Bilbo",
  "lastName" : "Baggins",
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/people/1"
    }
  }
}

次の例では、PATCH 呼び出しを使用しています。

$ curl -X PATCH -H "Content-Type:application/json" -d '{"firstName": "Bilbo Jr."}' http://localhost:8080/people/1
$ curl http://localhost:8080/people/1
{
  "firstName" : "Bilbo Jr.",
  "lastName" : "Baggins",
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/people/1"
    }
  }
}
PUT はレコード全体を置き換えます。指定されていないフィールドは null に置き換えられます。PATCH を使用して、アイテムのサブセットを更新できます。

次の例に示すように、レコードを削除することもできます。

$ curl -X DELETE http://localhost:8080/people/1
$ curl http://localhost:8080/people
{
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/people{?page,size,sort}",
      "templated" : true
    },
    "search" : {
      "href" : "http://localhost:8080/people/search"
    }
  },
  "page" : {
    "size" : 20,
    "totalElements" : 0,
    "totalPages" : 0,
    "number" : 0
  }
}

このハイパーメディア駆動型インターフェースの便利な側面は、curl(または任意の REST クライアント)を使用して、すべての RESTful エンドポイントを検出できることです。正式な契約書やインターフェースドキュメントを顧客と交換する必要はありません。

要約

おめでとう!ハイパーメディアベースの RESTful フロントエンドと JPA ベースのバックエンドを備えたアプリケーションを開発しました。