Spring Boot on GCP
  • Introduction
  • Getting Started
    • Google Cloud Platform
    • Cloud Shell
    • gcloud CLI
    • Hello World!
      • Cloud Shell
      • App Engine
      • Cloud Run
      • Kubernetes Engine
      • Compute Engine
      • Cloud Functions
  • Application Development
    • Development Tools
    • Spring Cloud GCP
    • Cloud Services
      • Databases
        • Cloud SQL
        • Cloud Spanner
        • Cloud Firestore
          • Datastore Mode
          • Native Mode
      • Messaging
        • Cloud Pub/Sub
        • Kafka
      • Secret Management
      • Storage
      • Cache
        • Memorystore Redis
        • Memorystore Memcached (beta)
      • Other Services
    • Observability
      • Trace
      • Logging
      • Metrics
      • Profiling
      • Debugging
    • DevOps
      • Artifact Repository
  • Deployment
    • Runtime Environments
    • Container
      • Container Image
      • Secure Container Image
      • Container Awareness
      • Vulnerability Scanning
      • Attestation
    • Kubernetes
      • Kubernetes Cluster
      • Deployment
      • Resources
      • Service
      • Health Checks
      • Load Balancing
        • External Load Balancing
        • Internal Load Balancing
      • Scheduling
      • Workload Identity
      • Binary Authorization
    • Istio
      • Getting Started
      • Sidecar Proxy
  • Additional Resources
    • Code Labs
    • Presentations / Videos
    • Cheat Sheets
Powered by GitBook
On this page
  • Cloud Spanner Instance
  • Enable API
  • Create an Instance
  • Create a Database
  • Connect to Instance
  • Create a Table
  • Spring Data Spanner Starter
  • Dependency
  • Configuration
  • ORM
  • Repository
  • Rest Repository
  • Samples
  • JDBC
  • Dependency
  • Configuration
  • Hibernate
  • Dependency
  • Configuration
  • ORM
  • Repository
  • Rest Repository
  • Sample
  • R2DBC
  • Samples

Was this helpful?

  1. Application Development
  2. Cloud Services
  3. Databases

Cloud Spanner

PreviousCloud SQLNextCloud Firestore

Last updated 4 years ago

Was this helpful?

is a scalable, enterprise-grade, globally-distributed, and strongly consistent database service built for the cloud specifically to combine the benefits of relational database structure with non-relational horizontal scale. It delivers high-performance transactions and strong consistency across rows, regions, and continents with an industry-leading 99.999% availability SLA, no planned downtime, and enterprise-grade security. Cloud Spanner revolutionizes database administration and management and makes application development more efficient.

Cloud Spanner Instance

Enable API

gcloud services enable spanner.googleapis.com

Create an Instance

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

This example creates a new regional instance (i.e., spanning across zones within the same region). Cloud Spanner supports multi-regional configuration to span across multiple regions for highest availability.

Create a Database

A Cloud Spanner instance can host multiple databases, that each contains its own tables.

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

Connect to Instance

There is no interactive CLI to Cloud Spanner. You can create table and execute SQL statements from the Cloud Console, or from gcloud command line. Following are some common operations:

List Databases

gcloud spanner databases list --instance=spanner-instance

Execute SQL Statements

gcloud spanner databases execute-sql orders \
  --instance=spanner-instance \
  --sql="SELECT 1"

Show Query Plan

gcloud spanner databases execute-sql orders \
  --instance=spanner-instance \
  --sql="SELECT 1" \
  --query-mode=PLAN

Create a Table

Create a DDL file, 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;

Cloud Spanner differs from traditional RDBMS in a several ways:

  • No server-side automatic ID generation.

  • Avoid monodically increasing IDs - I.e., no auto incremented ID, because it may create hot spots in partitions.

  • No foreign key constraints.

Cloud Spanner, being horizontally scalable and shards data with your keys, prefers the following:

  • UUIDv4 as IDs - It's random and can be partitioned easily.

  • Parent-Children relationship encoded using a compose primary key.

  • Colocate Parent-Children data using Interleave tables - If accessing parent means children data is also likely to be read, interleaving allows children data to be colocated with parent row in the same parition.

Use gcloud to execute the DDL:

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

Spring Data Spanner Starter

Spring Data Feature

Supported

ORM

✅

Declarative Transaction

✅

Repository

✅

REST Repository

✅

Query methods

✅

Query annotation

✅

Pagination

✅

Events

✅

Auditing

✅

Dependency

Add the Spring Data Spanner starter:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-gcp-starter-data-spanner</artifactId>
</dependency>
compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-starter-data-spanner'

Configuration

Configure Cloud Spanner instance and database to connect to.

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

Notice that there is no explicit configuration for username/password. Cloud Spanner authentication uses the GCP credential (either your user credential, or Service Account credential), and authorization is configured via Identity Access Management (IAM).

ORM

Order.java
import java.time.LocalDateTime;
import java.util.List;
import lombok.Data;
import org.springframework.cloud.gcp.data.spanner.core.mapping.*;

@Table(name="orders")
@Data // Lombok to generate getter/setters
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;
}  
OrderItem.java
import lombok.Data;
import org.springframework.cloud.gcp.data.spanner.core.mapping.*;

@Table(name="order_items")
@Data // Lombok to generate getter/setters
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;
}

Repository

Use Spring Data repository to quickly get CRUD access to the Cloud Spanner tables.

OrderRepository.java
package com.example.demo;

import org.springframework.cloud.gcp.data.spanner.repository.SpannerRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface OrderRepository extends SpannerRepository<Order, String> {
}
OrderItemRepoistory.java
package com.example.demo;

import lombok.Data;
import org.springframework.cloud.gcp.data.spanner.core.mapping.*;

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

Order is the parent and has only a primary key. Spring Data repository's ID parameter type can be the type for the single key. OrderItem, however, has a composite key. Spring Data repository's ID parameter type must be Cloud Spanner's Key type for a composite key.

In a business logic service, you can utilize the repositories:

@Service
class OrderService {
  private final OrderRepository orderRepository;

  OrderService(OrderRepository orderRepository,
      OrderItemRepository orderItemRepository) {
    this.orderRepository = orderRepository;
  }

  @Transactional
  Order createOrder(Order order) {
    // Use UUID String representation for the ID
    order.setId(UUID.randomUUID().toString());

    // Set the creation time
    order.setCreationTimestamp(LocalDateTime.now());

    // Set the parent Order ID and children ID for each item.
    if (order.getItems() != null) {
        order.getItems().stream().forEach(orderItem -> {
        orderItem.setOrderId(order.getId());
        orderItem.setOrderItemId(UUID.randomUUID().toString());
      });
    }

    // Children are saved in cascade.
    return orderRepository.save(order);
  }
}

Rest Repository

Add Spring Data Rest starter:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-rest'
@RepositoryRestResource
interface OrderRepository extends SpannerRepository<Order, String> {
}

@RepositoryRestResource
interface OrderItemRepository extends SpannerRepository<OrderItem, Key> {
  List<OrderItem> findAllByOrderId(String orderId);
}

To access the endpoint for Order:

curl http://localhost:8080/orders

Samples

JDBC

Dependency

Add Cloud Spanner JDBC Driver:

<dependency>
    <groupId>com.google.cloud</groupId>
    <artifactId>google-cloud-spanner-jdbc</artifactId>
</dependency>
compile group: 'com.google.cloud', name: 'google-cloud-spanner-jdbc'

Use Spring Boot JDBC Starter to use JDBC Template:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
compile group: 'org.springframework.boot', name: 'spring-boot-starter-jdbc'

Configuration

Cloud Spanner JDBC Driver Class:

com.google.cloud.spanner.jdbc.JdbcDriver

Cloud Spanner JDBC URL format:

jdbc:cloudspanner:/projects/PROJECT_ID/instances/INSTANCE_ID/databases/DATABASE_NAME

With Spring Data JDBC, you can configure the datasource:

application.properties
spring.datasource.driver-class-name=com.google.cloud.spanner.jdbc.JdbcDriver
spring.datasource.url=jdbc:cloudspanner:/projects/PROJECT_ID/instances/spanner-instance/databases/orders

Hibernate

Dependency

Add both the Cloud Spanner JDBC driver and Cloud Spanner Hibernate Dialect:

<dependency>
  <groupId>com.google.cloud</groupId>
  <artifactId>google-cloud-spanner-jdbc</artifactId>
</dependency>
<dependency>
  <groupId>com.google.cloud</groupId>
  <artifactId>google-cloud-spanner-hibernate-dialect</artifactId>
</dependency>
compile group: 'com.google.cloud', name: 'google-cloud-spanner-jdbc'
compile group: 'com.google.cloud', name: 'google-cloud-spanner-hibernate-dialect'

Add Spring Data JPA starter:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa'

Configuration

Cloud Spanner Hibernate Dialect class:

com.google.cloud.spanner.hibernate.SpannerDialect

Configure Spring Data JPA:

application.properties
spring.datasource.driver-class-name=com.google.cloud.spanner.jdbc.JdbcDriver
spring.datasource.url=jdbc:cloudspanner:/projects/PROJECT_ID/instances/spanner-instance/databases/orders

spring.jpa.database-platform=com.google.cloud.spanner.hibernate.SpannerDialect

ORM

Use JPA annotations to map POJO to Cloud Spanner database tables.

In Cloud Spanner, parent-children relationship is modeled as a composite key. With JPA, use IdClass or EmbeddableId to map a composite key.

@Entity
@Table(name="orders")
class Order {
    @Id
    @Column(name="order_id")
  private String id;

    private String description;

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

    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
    private List<OrderItem> items;

  // Getter and Setter ...
}

@Embeddable
class OrderItemId implements Serializable {
  @Column(name="order_id")
    private String orderId;

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

    // Getter and Setter ...
    // Hashcode and Equals ...
}

@Entity
@Table(name="order_items")
class OrderItem {
    @EmbeddedId
    private OrderItemId orderItemId;

    private String description;
    private Long quantity;

    @ManyToOne
  @JoinColumn(name="order_id", insertable = false, updatable = false)
    private Order order;

    // Getter and Setter ...
}

Repository

Use Spring Data repository for CRUD access to the tables.

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

@Repository
interface OrderItemRepository extends JpaRepository<OrderItem, OrderItemId> {
}

Rest Repository

Use Spring Data Rest to expose the repositories as RESTful services with HATEOS format.

@RepositoryRestResource
interface OrderRepository extends JpaRepository<Order, String> {
}

@RepositoryRestResource
interface OrderItemRepository extends JpaRepository<OrderItem, OrderItemId> {
}

Sample

R2DBC

Samples

The easiest way to access Cloud Spanner is using Spring Cloud GCP's . This starter provides full Spring Data support for Cloud Spanner while implementing idiomatic access patterns.

Spring Data Cloud Spanner allows you to map domain POJOs to Cloud Spanner tables via annotations. Read the for details

Read the for more details.

can expose a Spring Data repository directly on a RESTful endpoint, and rendering the payload as JSON with format. It supports common access patterns like pagination.

can be used if you need raw JDBC access.

lets you use Cloud Spanner with . You can use Cloud Spanner with Hibernate from any Java application. When using Spring Boot, you can use the Hibernate Dialect with .

let's you access Cloud Spanner data with reactive API.

is under active development.

It can be used with using the .

Cloud Spanner
Spring Data Spanner starter
Spring Data Spanner reference documentation
Spring Data Spanner reference documentation
Spring Data Rest
HATEOS
Spring Boot with Cloud Spanner sample
Cloud Spanner JDBC Driver
Cloud Spanner Hibernate Dialect
Hibernate ORM
Spring Data JPA
Spring Boot with Spring Data JPA and Cloud Spanner
Quarkus with Hibernate and Cloud Spanner
Microprofile with Hibernate and Cloud Spanner
Cloud Spanner R2DBC driver
Cloud Spanner R2DBC driver
Spring Data R2DBC
Cloud Spanner R2DBC Dialect
Spring Boot with Spring Data R2DBC and Cloud Spanner