整合 Spanner 與 Spring Data

Spring Data Spanner 模組可協助您在以 Spring Framework 建構的任何 Java 應用程式中使用 Spanner。

與所有 Spring Data 模組一樣,Spring Data Spanner 提供以 Spring 為基礎的程式設計模型,可保留 Spanner 的一致性保證和擴充性。其功能與 Spring Data JPAHibernate ORM 類似,但註解是專為 Spanner 設計。如要進一步瞭解如何搭配使用 Spring Data JPA 與 Spanner,請參閱「整合 Spanner 與 Spring Data JPA (GoogleSQL 方言)」。

如果您已熟悉 Spring,則 Spring Data Spanner 可讓您更輕鬆地在應用程式中使用 Spanner,並減少需要編寫的程式碼量。

本頁說明如何將 Spring Data Spanner 新增至 Java 應用程式。如要進一步瞭解這個模組,請參閱 Spring Data Spanner 參考資料

安裝模組

如果您使用 Maven,請將 Spring Cloud GCP Bill of Materials (BOM) 和 Spring Data Spanner 新增至 pom.xml 檔案。這些依附元件會將 Spring Data Spanner 元件提供給 Spring ApplicationContext

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>com.google.cloud</groupId>
      <artifactId>spring-cloud-gcp-dependencies</artifactId>
      <version>3.7.7</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-dependencies</artifactId>
      <version>${spring.boot.version}</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

<dependencies>
  <dependency>
    <groupId>com.google.cloud</groupId>
    <artifactId>spring-cloud-gcp-starter-data-spanner</artifactId>
  </dependency>
</dependencies>

您也必須建立服務帳戶,並使用服務帳戶金鑰向 Google Cloud進行驗證。

詳情請參閱設定 Java 開發環境的操作說明。您不需要安裝 Google Cloud Java 專用的用戶端程式庫,Spring Boot 啟動器會自動安裝。

設定模組

本節說明 Spring Data Spanner 最常用的設定。如需完整設定清單,請參閱參考說明文件

指定執行個體和資料庫

如要指定預設執行個體和資料庫,請為應用程式設定下列設定屬性:

屬性 說明
spring.cloud.gcp.spanner.project-id (選用步驟) 專案 ID。 Google Cloud 覆寫 spring.cloud.gcp.config.project-id 的值。
spring.cloud.gcp.spanner.instance-id Spanner 執行個體 ID。
spring.cloud.gcp.spanner.database 要連線的資料庫。

建立 Spanner 資料模型

透過 Spring Data Spanner,您可以使用簡單傳統的 Java 物件 (POJO) 建立模型,將資料儲存在 Spanner 資料表中。

針對資料庫中的每個資料表,宣告代表該資料表中記錄的實體。使用註解將實體及其屬性對應至資料表和資料欄。

您可以使用下列註解,為實體和表格之間的簡單關係建立模型:

實體註解
@Column(name = "columnName")

(選用步驟) 將屬性對應至 Spanner 資料表中的特定資料欄,覆寫會自動對應名稱的命名策略。

如果省略這個屬性,Spring Data Spanner 會根據預設命名策略,將 Java camelCase 屬性名稱對應至 PascalCase 資料欄名稱。舉例來說,屬性 singerId 會對應至欄名 SingerId

@Embedded

表示該屬性是可保留主鍵元件的內嵌物件。如果屬性實際用於主鍵,您也必須加入 @PrimaryKey 註解。

@Interleaved

@Interleaved(lazy = true)

指出屬性包含與目前資料列交錯的資料列清單。

根據預設,Spring Data Spanner 會在建立執行個體時擷取交錯資料列。如要在存取屬性時延遲擷取資料列,請使用 @Interleaved(lazy = true)

範例:如果 Singer 實體可以有交錯的 Album 項目做為子項,請在 Singer 實體中新增 List<Album> 屬性。此外,請在屬性中加入 @Interleaved 註解。

@NotMapped

指出屬性不會儲存在資料庫中,應予以忽略。

@PrimaryKey

@PrimaryKey(keyOrder = N)

表示屬性是主鍵的元件,並識別屬性在主鍵中的位置,從 1 開始。keyOrder 預設為 1

範例:@PrimaryKey(keyOrder = 3)

@Table(name = "TABLE_NAME")

實體所模擬的資料表。實體的每個例項都代表資料表中的一筆記錄。將 TABLE_NAME 換成資料表名稱。

範例:@Table(name = "Singers")

如需建立更複雜的關係模型,請參閱 Spring Data Spanner 參考資料,瞭解這個模組支援的其他註解。

下列範例說明如何為 Spring Data Spanner 建立 SingersAlbums 資料表的模型:

  • 如果是 Singer 實體,範例會包含 albums 屬性,以及 @Interleaved 註解。這個屬性包含與 Singer 實體交錯的專輯清單。Spring Data Spanner 會自動填入這個屬性。
  • Album 實體為例,其中包含未儲存在 Spanner 中的 relatedAlbums 屬性。
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;
import java.util.Date;
import java.util.List;


/**
 * An entity and table holding singers.
 */
@Table(name = "Singers")
public class Singer {
  @PrimaryKey
  long singerId;

  String firstName;

  String lastName;

  Date birthDate;

  @Interleaved
  List<Album> albums;
}
import com.google.cloud.spring.data.spanner.core.mapping.NotMapped;
import com.google.cloud.spring.data.spanner.core.mapping.PrimaryKey;
import com.google.cloud.spring.data.spanner.core.mapping.Table;
import java.util.List;

/**
 * An entity class representing an Album.
 */
@Table(name = "Albums")
public class Album {

  @PrimaryKey
  long singerId;

  @PrimaryKey(keyOrder = 2)
  long albumId;

  String albumTitle;

  long marketingBudget;

  @NotMapped
  List<Album> relatedAlbums;

  public Album(long singerId, long albumId, String albumTitle, long marketingBudget) {
    this.singerId = singerId;
    this.albumId = albumId;
    this.albumTitle = albumTitle;
    this.marketingBudget = marketingBudget;
  }
}

查詢及修改資料

如要使用 Spring Data Spanner 查詢及修改資料,可以取得 SpannerTemplate bean,該 bean 會實作 SpannerOperationsSpannerTemplate 提供執行 SQL 查詢的方法,以及使用資料操縱語言 (DML) 陳述式修改資料的方法。您也可以使用這個 Bean 存取 Spanner 的讀取 API突變 API

此外,您還可以擴充 SpannerRepository 介面,封裝所有查詢及修改 Spanner 資料的應用程式邏輯。

以下各節說明如何使用 SpannerTemplateSpannerRepository

取得範本 Bean

使用 @Autowired 註解自動取得 SpannerTemplate Bean。然後在整個類別中使用 SpannerTemplate

以下範例顯示取得及使用 Bean 的類別:

import com.google.cloud.spanner.KeySet;
import com.google.cloud.spanner.Statement;
import com.google.cloud.spring.data.spanner.core.SpannerQueryOptions;
import com.google.cloud.spring.data.spanner.core.SpannerTemplate;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * A quick start code for Spring Data Cloud Spanner. It demonstrates how to use SpannerTemplate to
 * execute DML and SQL queries, save POJOs, and read entities.
 */
@Component
public class SpannerTemplateSample {

  @Autowired
  SpannerTemplate spannerTemplate;

  public void runTemplateExample(Singer singer) {
    // Delete all of the rows in the Singer table.
    this.spannerTemplate.delete(Singer.class, KeySet.all());

    // Insert a singer into the Singers table.
    this.spannerTemplate.insert(singer);

    // Read all of the singers in the Singers table.
    List<Singer> allSingers = this.spannerTemplate
        .query(Singer.class, Statement.of("SELECT * FROM Singers"),
                new SpannerQueryOptions().setAllowPartialRead(true));
  }

}

您可以使用 SpannerTemplate Bean 執行唯讀交易讀寫交易。此外,您可以使用 @Transactional 註解建立宣告式交易。

取得存放區 Bean

如果您使用 SpannerRepository,可以透過 @Autowired 註解取得實作存放區介面的 Bean。存放區包含以唯讀交易讀寫交易形式執行 Java 函式的方法。如要進行較低層級的作業,可以取得存放區使用的範本 Bean。

以下範例顯示存放區的介面,以及取得並使用 Bean 的類別:

import com.google.cloud.spanner.Key;
import com.google.cloud.spring.data.spanner.repository.SpannerRepository;
import com.google.cloud.spring.data.spanner.repository.query.Query;
import java.util.List;
import org.springframework.data.repository.query.Param;


/**
 * An interface of various Query Methods. The behavior of the queries is defined only by
 * their names, arguments, or annotated SQL strings. The implementation of these functions
 * is generated by Spring Data Cloud Spanner.
 */
public interface SingerRepository extends SpannerRepository<Singer, Key> {
  List<Singer> findByLastName(String lastName);

  int countByFirstName(String firstName);

  int deleteByLastName(String lastName);

  List<Singer> findTop3DistinctByFirstNameAndSingerIdIgnoreCaseOrLastNameOrderByLastNameDesc(
      String firstName, String lastName, long singerId);

  @Query("SELECT * FROM Singers WHERE firstName LIKE '%@fragment';")
  List<Singer> getByQuery(@Param("fragment") String firstNameFragment);
}
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * A quick start code for Spring Data Cloud Spanner.
 * It demonstrates how to use a SpannerRepository to execute read-write queries
 * generated from interface definitions.
 *
 */
@Component
public class SpannerRepositorySample {

  @Autowired
  SingerRepository singerRepository;

  public void runRepositoryExample() {
    List<Singer> lastNameSingers = this.singerRepository.findByLastName("a last name");

    int fistNameCount = this.singerRepository.countByFirstName("a first name");

    int deletedLastNameCount = this.singerRepository.deleteByLastName("a last name");
  }

}

管理 Spanner

如要取得 Spanner 資料庫的相關資訊、使用資料定義語言 (DDL) 陳述式更新結構定義,或完成其他管理工作,可以取得 SpannerDatabaseAdminTemplate bean。

使用 @Autowired 註解自動取得 Bean。然後在整個類別中使用 SpannerDatabaseAdminTemplate

以下範例顯示取得及使用 Bean 的類別:

import com.google.cloud.spring.data.spanner.core.admin.SpannerDatabaseAdminTemplate;
import com.google.cloud.spring.data.spanner.core.admin.SpannerSchemaUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * This sample demonstrates how to generate schemas for interleaved tables from POJOs and how to
 * execute DDL.
 */
@Component
public class SpannerSchemaToolsSample {

  @Autowired
  SpannerDatabaseAdminTemplate spannerDatabaseAdminTemplate;

  @Autowired
  SpannerSchemaUtils spannerSchemaUtils;

  /**
   * Creates the Singers table. Also creates the Albums table, because Albums is interleaved with
   * Singers.
   */
  public void createTableIfNotExists() {
    if (!this.spannerDatabaseAdminTemplate.tableExists("Singers")) {
      this.spannerDatabaseAdminTemplate.executeDdlStrings(
          this.spannerSchemaUtils
              .getCreateTableDdlStringsForInterleavedHierarchy(Singer.class),
          true);
    }
  }

  /**
   * Drops both the Singers and Albums tables using just a reference to the Singer entity type ,
   * because they are interleaved.
   */
  public void dropTables() {
    if (this.spannerDatabaseAdminTemplate.tableExists("Singers")) {
      this.spannerDatabaseAdminTemplate.executeDdlStrings(
          this.spannerSchemaUtils.getDropTableDdlStringsForInterleavedHierarchy(Singer.class),
          false);
    }
  }
}

後續步驟