Cloud Spanner を使用した Spring Boot アプリケーション

1. 概要

Cloud Spanner は、可用性が高く水平スケーリングが可能なマルチリージョン RDBMS です。この Codelab では Cloud Spanner の最小インスタンスを使用しますが、作業が終了したら必ずシャットダウンしてください。

学習内容

  • Cloud Spanner を使用して Spring Boot でデータを保存、取得する方法

必要なもの

  • Google Cloud Platform プロジェクト
  • ブラウザ(ChromeFirefox など)

このチュートリアルの利用方法をお選びください。

通読するのみ 通読し、演習を行う

Google Cloud Platform サービスのご利用経験についてどのように評価されますか?

初心者 中級者 上級者

2. 設定と要件

セルフペース型の環境設定

  1. Cloud Console にログインし、新しいプロジェクトを作成するか、既存のプロジェクトを再利用します(Gmail アカウントまたは G Suite アカウントをお持ちでない場合は、アカウントを作成する必要があります)。

dMbN6g9RawQj_VXCSYpdYncY-DbaRzr2GbnwoV7jFf1u3avxJtmGPmKpMYgiaMH-qu80a_NJ9p2IIXFppYk8x3wyymZXavjglNLJJhuXieCem56H30hwXtd8PvXGpXJO9gEUDu3cZw

ci9Oe6PgnbNuSYlMyvbXF1JdQyiHoEgnhl4PlV_MFagm2ppzhueRkqX4eLjJllZco_2zCp0V0bpTupUSKji9KkQyWqj11pqit1K1faS1V6aFxLGQdkuzGp4rsQTan7F01iePL5DtqQ

8-tA_Lheyo8SscAVKrGii2coplQp2_D1Iosb2ViABY0UUO1A8cimXUu6Wf1R9zJIRExL5OB2j946aIiFtyKTzxDcNnuznmR45vZ2HMoK3o67jxuoUJCAnqvEX6NgPGFjCVNgASc-lg

プロジェクト ID を忘れないようにしてください。プロジェクト ID はすべての Google Cloud プロジェクトを通じて一意の名前にする必要があります(上記の名前はすでに使用されているので使用できません)。以降、このコードラボでは PROJECT_ID と呼びます。

  1. 次に、Google Cloud リソースを使用するために、Cloud Console で課金を有効にする必要があります。

このコードラボを実行しても、費用はほとんどかからないはずです。このチュートリアル以外で請求が発生しないように、リソースのシャットダウン方法を説明する「クリーンアップ」セクションの手順に従うようにしてください。Google Cloud の新規ユーザーは、300 米ドル分の無料トライアル プログラムをご利用いただけます。

Cloud Shell をアクティブにする

  1. Cloud Console で、[Cloud Shell をアクティブにする] H7JlbhKGHITmsxhQIcLwoe5HXZMhDlYue4K-SPszMxUxDjIeWfOHBfxDHYpmLQTzUmQ7Xx8o6OJUlANnQF0iBuUyfp1RzVad_4nCa0Zz5LtwBlUZFXFCWFrmrWZLqg1MkZz2LdgUDQ をクリックします。

zlNW0HehB_AFW1qZ4AyebSQUdWm95n7TbnOr7UVm3j9dFcg6oWApJRlC0jnU1Mvb-IQp-trP1Px8xKNwt6o3pP6fyih947sEhOFI4IRF0W7WZk6hFqZDUGXQQXrw21GuMm2ecHrbzQ

Cloud Shell を起動したことがない場合、その内容を説明する中間画面が(スクロールしなければ見えない範囲に)が表示されます。その場合は、[続行] をクリックします(以後表示されなくなります)。このワンタイム スクリーンは次のようになります。

kEPbNAo_w5C_pi9QvhFwWwky1cX8hr_xEMGWySNIoMCdi-Djx9AQRqWn-__DmEpC7vKgUtl-feTcv-wBxJ8NwzzAp7mY65-fi2LJo4twUoewT1SUjd6Y3h81RG3rKIkqhoVlFR-G7w

Cloud Shell のプロビジョニングと接続に少し時間がかかる程度です。

pTv5mEKzWMWp5VBrg2eGcuRPv9dLInPToS-mohlrqDASyYGWnZ_SwE-MzOWHe76ZdCSmw0kgWogSJv27lrQE8pvA5OD6P1I47nz8vrAdK7yR1NseZKJvcxAZrPb8wRxoqyTpD-gbhA

この仮想マシンには、必要な開発ツールがすべて準備されています。5 GB の永続ホーム ディレクトリが用意されており、Google Cloud で稼働するため、ネットワーク パフォーマンスが充実しており認証もスムーズです。このコードラボでの作業のほとんどは、ブラウザまたは Chromebook から実行できます。

Cloud Shell に接続すると、すでに認証は完了しており、プロジェクトに各自のプロジェクト ID が設定されていることがわかります。

  1. Cloud Shell で次のコマンドを実行して、認証されたことを確認します。
gcloud auth list

コマンド出力

 Credentialed Accounts
ACTIVE  ACCOUNT
*       <my_account>@<my_domain.com>

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
gcloud config list project

コマンド出力

[core]
project = <PROJECT_ID>

上記のようになっていない場合は、次のコマンドで設定できます。

gcloud config set project <PROJECT_ID>

コマンド出力

Updated property [core/project].

3. Cloud Spanner を初期化する

gcloud CLI を使用して Cloud Spanner API を有効にします。

gcloud services enable spanner.googleapis.com

Cloud Spanner インスタンスを作成します。

gcloud spanner instances create spanner-instance \
 --config=regional-us-central1 \
 --nodes=1 --description="A Spanner Instance"

インスタンス内にデータベースを作成します。

gcloud spanner databases create orders \
  --instance=spanner-instance

データスキーマを記述する schema.ddl ファイルを作成します。

cat << EOF > schema.ddl
CREATE TABLE orders (
  order_id STRING(36) NOT NULL,
  description STRING(255),
  creation_timestamp TIMESTAMP,
) PRIMARY KEY (order_id);

CREATE TABLE order_items (
  order_id STRING(36) NOT NULL,
  order_item_id STRING(36) NOT NULL,
  description STRING(255),
  quantity INT64,
) PRIMARY KEY (order_id, order_item_id),
  INTERLEAVE IN PARENT orders ON DELETE CASCADE;
EOF

Cloud Spanner データベースにスキーマを適用します。

gcloud spanner databases ddl update orders \
  --instance=spanner-instance \
  --ddl="$(<schema.ddl)"

4. 新しい Spring Boot Java アプリケーションをブートストラップする

Cloud Shell 環境から次のコマンドを使用して、新しい Spring Boot アプリケーションを初期化し、ブートストラップします。

$ curl https://start.spring.io/starter.tgz \
  -d packaging=jar \
  -d dependencies=cloud-gcp,web,lombok \
  -d baseDir=spanner-example \
  -d type=maven-project \
  -d bootVersion=3.2.6 | tar -xzvf -

$ cd spanner-example

これにより、新しい Maven プロジェクトを含む新しい spanner-example/ ディレクトリ、Maven の pom.xml、Maven ラッパー、アプリケーション エントリポイントが作成されます。

pom.xml ファイルで、Spring Data Cloud Spanner スターターを追加します。

spanner-example/pom.xml

<project>
  ...
  <dependencies>
        ...
        <!-- Add Spring Cloud GCP Spanner Starter -->
        <dependency>
                <groupId>com.google.cloud</groupId>
                <artifactId>spring-cloud-gcp-starter-data-spanner</artifactId>
        </dependency>

        ...

  </dependencies>

  ...
</project>

application.properties で、Spanner データベースの接続情報を構成します。

spanner-example/src/main/resources/application.properties

spring.cloud.gcp.spanner.instance-id=spanner-instance
spring.cloud.gcp.spanner.database=orders

JAVA_HOME が正しいバージョンに設定されていることを確認します。

export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64/

アプリを再ビルドして、Maven の構成が正しいことを確認します。

./mvnw package

5. エンティティを作成する

Spring Cloud GCP の Spring Data Spanner サポートにより、Spring Data を使用して Java オブジェクトと Spanner テーブルへの慣用的な ORM マッピングを簡単に作成できます。

まず、Order Item クラスを作成します。

spanner-example/src/main/java/com/example/demo/OrderItem.java

package com.example.demo;

import com.google.cloud.spring.data.spanner.core.mapping.Column;
import com.google.cloud.spring.data.spanner.core.mapping.PrimaryKey;
import com.google.cloud.spring.data.spanner.core.mapping.Table;

@Table(name="order_items")
@Data
class OrderItem {
  @PrimaryKey(keyOrder = 1)
  @Column(name="order_id")
  private String orderId;

  @PrimaryKey(keyOrder = 2)
  @Column(name="order_item_id")
  private String orderItemId;

  private String description;
  private Long quantity;
}

Spanner の親子関係には、複合主キーを使用する必要があります。この例では、複合キーは order_idorder_item_id です。

次に、Order クラスを作成します。

spanner-example/src/main/java/com/example/demo/Order.java

package com.example.demo;

import java.time.LocalDateTime;
import java.util.List;
import lombok.Data;
import com.google.cloud.spring.data.spanner.core.mapping.Column;
import com.google.cloud.spring.data.spanner.core.mapping.Interleaved;
import com.google.cloud.spring.data.spanner.core.mapping.PrimaryKey;
import com.google.cloud.spring.data.spanner.core.mapping.Table;

@Table(name="orders")
@Data
public class Order {
  @PrimaryKey
  @Column(name="order_id")
  private String id;

  private String description;

  @Column(name="creation_timestamp")
  private LocalDateTime timestamp;

  @Interleaved
  private List<OrderItem> items;
}

このクラスでは、@Interleaved アノテーションを使用して、Order Item との 1 対多のリレーションを作成します。

6. OrderRepository インターフェースを作成する

次の内容で OrderRepository クラスを作成します。

spanner-example/src/main/java/com/example/demo/OrderRepository.java

package com.example.demo;

import com.google.cloud.spring.data.spanner.repository.SpannerRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface OrderRepository extends SpannerRepository<Order, String> {
}

このインターフェースは SpannerRepository<Order, String> を拡張します。ここで、Order はドメインクラス、String は主キーの型です。Spring Data はこのインターフェースを通じて自動的に CRUD アクセスを提供するため、追加のコードを作成する必要はありません。

7. 基本オペレーション用の REST コントローラを作成する

メイン アプリケーションの DemoApplication クラスを開き、次のように変更します。

spanner-example/src/main/java/com/example/demo/DemoApplication.java

package com.example.demo;

import java.time.LocalDateTime;
import java.util.UUID;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ResponseStatusException;

@SpringBootApplication
public class DemoApplication {
        public static void main(String[] args) {
                SpringApplication.run(DemoApplication.class, args);
        }
}

@RestController
class OrderController {
  private final OrderRepository orderRepository;

        OrderController(OrderRepository orderRepository) {
                this.orderRepository = orderRepository;
        }

        @GetMapping("/api/orders/{id}")
        public Order getOrder(@PathVariable String id) {
          return orderRepository.findById(id)
                                .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, id + " not found"));
        }

        @PostMapping("/api/orders")
        public String createOrder(@RequestBody Order order) {
                // Spanner currently does not auto generate IDs
                // Generate UUID on new orders
                order.setId(UUID.randomUUID().toString());
                order.setTimestamp(LocalDateTime.now());

                order.getItems().forEach(item -> {
                        // Assign parent ID, and also generate child ID
                        item.setOrderId(order.getId());
                        item.setOrderItemId(UUID.randomUUID().toString());
                });

          Order saved = orderRepository.save(order);
          return saved.getId();
        }
}

8. アプリケーションを実行する

アプリケーションを再ビルドして実行します。

./mvnw spring-boot:run

正常に開始され、ポート 8080 をリッスンするはずです。

注文レコードをエンドポイントに送信することができます。

curl -H"Content-Type: application/json" -d'{"description": "My orders", "items": [{"description": "Android Phone", "quantity": "1"}]}' \
  http://localhost:8080/api/orders

オーダーの UUID が返されます。

その後、UUID を使用して Order を取得できます。

curl http://localhost:8080/api/orders/REPLACE_WITH_ORDER_UUID

Cloud Spanner にデータがどのように保存されているかを確認するには、Cloud コンソールに移動して [Spanner] → [Spanner インスタンス] → [注文データベース] → [注文テーブル] → [データ] に移動します。

780f4947e4a864f6.png

9. クリーンアップ

クリーンアップするには、Spanner インスタンスを削除して、料金が発生しないようにします。

gcloud spanner instances delete spanner-instance -q

10. 完了

この Codelab では、Cloud Spanner にデータを保存および取得できるインタラクティブな CLI アプリケーションを作成しました。

詳細

ライセンス

この作業はクリエイティブ・コモンズの表示 2.0 汎用ライセンスにより使用許諾されています。