将 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 物料清单 (BOM) 和 Spring Data Spanner 添加到您的 pom.xml 文件中。这些依赖项为您的 Spring ApplicationContext 提供了 Spring Data Spanner 组件:

<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 开发环境的说明。您无需安装适用于 Java 的 Google Cloud 客户端库;Spring Boot 入门版会自动安装该客户端库。

配置模块

本部分介绍了 Spring Data Spanner 的一些最常用的配置设置。如需查看设置的完整列表,请参阅参考文档

指定实例和数据库

如需指定默认实例和数据库,请为您的应用设置以下配置属性:

属性 说明
spring.cloud.gcp.spanner.project-id 可选。Google Cloud 项目 ID。替换 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 条目作为子级,请将 List<Album> 属性添加到 Singer 实体。此外,请将 @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 查询和修改数据,您可以获取实现 SpannerOperationsSpannerTemplate Bean。SpannerTemplate 提供了一些使用数据操纵语言 (DML) 语句执行 SQL 查询和修改数据的方法。您还可以使用此 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);
    }
  }
}

后续步骤