Applicazione Spring Boot con Cloud Spanner

1. Panoramica

Cloud Spanner è un RDBMS a disponibilità elevata, scalabile orizzontalmente e multiregionale. Questo lab di codice utilizzerà un'istanza di Cloud Spanner più piccola, ma non dimenticare di arrestarlo quando hai finito.

Cosa imparerai a fare

  • Come utilizzare Cloud Spanner per salvare e recuperare i dati con Spring Boot

Che cosa ti serve

  • Un progetto Google Cloud
  • Un browser, ad esempio Chrome o Firefox

Come utilizzerai questo tutorial?

Solo lettura Leggi e completa gli esercizi

Come giudichi la tua esperienza di utilizzo dei servizi della piattaforma Google Cloud?

Principiante Livello intermedio Livello avanzato

2. Configurazione e requisiti

Configurazione dell'ambiente da seguire in modo autonomo

  1. Accedi alla console Cloud e crea un nuovo progetto o riutilizzane uno esistente. Se non hai ancora un account Gmail o G Suite, devi crearne uno.

dMbN6g9RawQj_VXCSYpdYncY-DbaRzr2GbnwoV7jFf1u3avxJtmGPmKpMYgiaMH-qu80a_NJ9p2IIXFppYk8x3wyymZXavjglNLJJhuXieCem56H30hwXtd8PvXGpXJO9gEUDu3cZw

ci9Oe6PgnbNuSYlMyvbXF1JdQyiHoEgnhl4PlV_MFagm2ppzhueRkqX4eLjJllZco_2zCp0V0bpTupUSKji9KkQyWqj11pqit1K1faS1V6aFxLGQdkuzGp4rsQTan7F01iePL5DtqQ

8-tA_Lheyo8SscAVKrGii2coplQp2_D1Iosb2ViABY0UUO1A8cimXUu6Wf1R9zJIRExL5OB2j946aIiFtyKTzxDcNnuznmR45vZ2HMoK3o67jxuoUJCAnqvEX6NgPGFjCVNgASc-lg

Ricorda l'ID progetto, un nome univoco in tutti i progetti Google Cloud (il nome precedente è già stato utilizzato e non funzionerà correttamente). Verrà indicato più avanti in questo codelab come PROJECT_ID.

  1. Successivamente, dovrai abilitare la fatturazione in Cloud Console per utilizzare le risorse Google Cloud.

Eseguire questo codelab non dovrebbe costare molto. Assicurati di seguire tutte le istruzioni riportate nella sezione "Pulizia" che ti suggerisce come arrestare le risorse per evitare addebiti successivi a questo tutorial. I nuovi utenti di Google Cloud sono idonei al programma prova senza costi di 300$.

Attiva Cloud Shell

  1. Dalla console Cloud, fai clic su Attiva Cloud Shell H7JlbhKGHITmsxhQIcLwoe5HXZMhDlYue4K-SPszMxUxDjIeWfOHBfxDHYpmLQTzUmQ7Xx8o6OJUlANnQF0iBuUyfp1RzVad_4nCa0Zz5LXZ.

zlNW0HehB_AFW1qZ4AyebSQUdWm95n7TbnOr7UVm3j9dFcg6oWApJRlC0jnU1Mvb-IQp-trP1Px8xKNwt6o3pP6fyih947sEhOFI4IRF0W7WZk6hFqZDUGXQQXrw21GuMm2ecHrbzQ

Se non hai mai avviato Cloud Shell, verrà visualizzata una schermata intermedia (below the fold) che descrive di cosa si tratta. In questo caso, fai clic su Continua (non lo vedrai più). Ecco come appare quella singola schermata:

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

Il provisioning e la connessione a Cloud Shell dovrebbero richiedere solo qualche istante.

pTv5mEKzWMWp5VBrg2eGcuRPv9dLInPToS-mohlrqDASyYGWnZ_SwE-MzOWHe76ZdCSmw0kgWogSJv27lrQE8pvA5OD6P1I47nz8vrAdK7yR1NseZKJvcxAZrPb8wRxoqyTpD-gbhA

Questa macchina virtuale viene caricata con tutti gli strumenti di sviluppo necessari. Offre una home directory permanente da 5 GB e viene eseguita in Google Cloud, migliorando notevolmente le prestazioni di rete e l'autenticazione. Gran parte, se non tutto, del lavoro in questo codelab può essere svolto semplicemente con un browser o Chromebook.

Una volta eseguita la connessione a Cloud Shell, dovresti vedere che il tuo account è già autenticato e il progetto è già impostato sul tuo ID progetto.

  1. Esegui questo comando in Cloud Shell per verificare che l'account sia autenticato:
gcloud auth list

Output comando

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

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

Output comando

[core]
project = <PROJECT_ID>

In caso contrario, puoi impostarlo con questo comando:

gcloud config set project <PROJECT_ID>

Output comando

Updated property [core/project].

3. Inizializza Cloud Spanner

Abilita l'API Cloud Spanner utilizzando gcloud CLI:

gcloud services enable spanner.googleapis.com

Crea un'istanza Cloud Spanner:

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

Crea un database all'interno dell'istanza:

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

Crea un file schema.ddl per descrivere lo schema di dati:

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

Applica lo schema al database Cloud Spanner:

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

4. Esegui il bootstrap di una nuova applicazione Java Spring Boot

Dall'ambiente Cloud Shell, utilizza il comando seguente per inizializzare e avviare una nuova applicazione Spring Boot con il bootstrap:

$ 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

Verrà creata una nuova directory spanner-example/ con un nuovo progetto Maven, insieme al pom.xml di Maven, un wrapper Maven e un punto di ingresso dell'applicazione.

Nel file pom.xml, aggiungi il comando iniziale di Cloud Spanner Spring Data.

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>

In application.properties, configura le informazioni di connessione al database Spanner:

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

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

Assicurati che JAVA_HOME sia impostata sulla versione corretta:

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

Ricrea l'app per assicurarti che la configurazione Maven sia corretta:

./mvnw package

5. Crea le entità

Grazie al supporto di Spring Data Spanner di Spring Cloud Google Cloud, puoi creare facilmente un oggetto Java e una mappatura ORM idiomatica a una tabella Spanner utilizzando Spring Data.

Per prima cosa, crea una classe Articolo dell'ordine.

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;
}

Per le relazioni padre/figlio in Spanner, devi utilizzare una chiave primaria composita. In questo esempio, la chiave composita è order_id e order_item_id.

A questo punto, crea una classe 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;
}

Questa classe utilizza l'annotazione @Interleaved per creare una relazione uno-a-molti con gli articoli dell'ordine.

6. Crea l'interfaccia OrderRepository

Crea la classe OrderRepository con i seguenti contenuti:

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> {
}

L'interfaccia estende SpannerRepository<Order, String> dove Order è la classe del dominio e String è il tipo di chiave principale. Spring Data fornisce automaticamente l'accesso CRUD attraverso questa interfaccia e non sarà necessario creare altro codice.

7. crea un controller REST per le operazioni di base

Apri la classe DemoApplication dell'applicazione principale e modificala in modo che abbia il seguente aspetto:

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. Esegui l'applicazione

Ricrea ed esegui l'applicazione.

./mvnw spring-boot:run

Questa operazione dovrebbe avviarsi correttamente e rimanere in ascolto sulla porta 8080.

Puoi pubblicare un record di ordine nell'endpoint:

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

Dovrebbe rispondere con il UUID dell'ordine.

Dopodiché potrai recuperare l'ordine con UUID:

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

Per vedere come sono archiviati i dati in Cloud Spanner, vai alla console Cloud e vai a Spanner → Istanza Spanner → database degli ordini → tabella degli ordini → Dati.

780f4947e4a864f6.png

9. Esegui la pulizia

Per eseguire la pulizia, elimina l'istanza Spanner in modo che non sia più soggetta a addebiti.

gcloud spanner instances delete spanner-instance -q

10. Complimenti!

In questo codelab, hai creato un'applicazione dell'interfaccia a riga di comando interattiva in grado di archiviare e recuperare i dati da Cloud Spanner.

Scopri di più

Licenza

Questo lavoro è concesso in licenza ai sensi di una licenza Creative Commons Attribution 2.0 Generic.