Integre o Spanner com o Spring Data

O módulo Spring Data Spanner ajuda a usar o Spanner em qualquer aplicação Java criada com o Spring Framework.

Tal como todos os módulos do Spring Data, o Spring Data Spanner oferece um modelo de programação baseado no Spring que mantém as garantias de consistência e a escalabilidade do Spanner. As respetivas funcionalidades são semelhantes ao Spring Data JPA e ao Hibernate ORM, com anotações concebidas para o Spanner. Para mais informações sobre como usar o Spring Data JPA com o Spanner, consulte o artigo Integre o Spanner com o Spring Data JPA (dialeto GoogleSQL).

Se já conhece o Spring, o Spring Data Spanner pode facilitar o trabalho com o Spanner na sua aplicação e reduzir a quantidade de código que tem de escrever.

Esta página explica como adicionar o Spring Data Spanner a uma aplicação Java. Para ver informações detalhadas sobre o módulo, consulte a referência do Spring Data Spanner.

Instale o módulo

Se usar o Maven, adicione a lista de materiais (BOM) do Spring Cloud GCP e o Spring Data Spanner ao seu ficheiro pom.xml. Estas dependências fornecem os componentes do Spring Data Spanner ao seu 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>

Também tem de criar uma conta de serviço e usar a chave da conta de serviço para fazer a autenticação com Google Cloud.

Para mais informações, consulte as instruções para configurar um ambiente de desenvolvimento Java. Não precisa de instalar a Google Cloud biblioteca de cliente para Java. O Spring Boot Starter instala a biblioteca de cliente automaticamente.

Configure o módulo

Esta secção descreve algumas das definições de configuração mais usadas para o Spring Data Spanner. Para ver uma lista completa das definições, consulte a documentação de referência.

Especifique uma instância e uma base de dados

Para especificar a instância e a base de dados predefinidas, defina as seguintes propriedades de configuração para a sua aplicação:

Propriedade Descrição
spring.cloud.gcp.spanner.project-id Opcional. O Google Cloud ID do projeto. Substitui o valor de spring.cloud.gcp.config.project-id.
spring.cloud.gcp.spanner.instance-id O ID da instância do Spanner.
spring.cloud.gcp.spanner.database A base de dados à qual estabelecer ligação.

Modele dados do Spanner

Com o Spring Data Spanner, pode usar objetos Java simples (POJOs) para modelar os dados que armazena nas suas tabelas do Spanner.

Para cada tabela na sua base de dados, declare uma entidade que represente um registo nessa tabela. Use anotações para mapear a entidade e as respetivas propriedades para uma tabela e as respetivas colunas.

Pode usar as seguintes anotações para modelar relações simples entre entidades e tabelas:

Anotações de entidades
@Column(name = "columnName")

Opcional. Mapeia a propriedade para uma coluna específica na tabela do Spanner, substituindo a estratégia de nomenclatura que mapeia automaticamente os nomes.

Quando omite esta propriedade, a estratégia de nomenclatura predefinida para Spring Data Spanner mapeia os nomes das propriedades Java camelCase para os nomes das colunas PascalCase. Por exemplo, a propriedade singerId é mapeada para o nome da coluna SingerId.

@Embedded

Indica que a propriedade é um objeto incorporado que pode conter componentes de uma chave principal. Se a propriedade for efetivamente usada na chave primária, também tem de incluir a anotação @PrimaryKey.

@Interleaved

@Interleaved(lazy = true)

Indica que uma propriedade contém uma lista de linhas que estão intercaladas com a linha atual.

Por predefinição, o Spring Data Spanner obtém as linhas intercaladas na criação da instância. Para obter as linhas de forma diferida, quando acede à propriedade, use @Interleaved(lazy = true).

Exemplo: se uma entidade Singer puder ter entradas Album intercaladas como filhos, adicione uma propriedade List<Album> à entidade Singer. Além disso, adicione uma anotação @Interleaved à propriedade.

@NotMapped

Indica que uma propriedade não está armazenada na base de dados e deve ser ignorada.

@PrimaryKey

@PrimaryKey(keyOrder = N)

Indica que a propriedade é um componente da chave principal e identifica a posição da propriedade na chave principal, começando em 1. O valor predefinido de keyOrder é 1.

Exemplo: @PrimaryKey(keyOrder = 3)

@Table(name = "TABLE_NAME")

A tabela que a entidade modela. Cada instância da entidade representa um registo na tabela. Substitua TABLE_NAME pelo nome da sua tabela.

Exemplo: @Table(name = "Singers")

Se precisar de modelar relações mais complexas, consulte a referência do Spring Data Spanner para ver detalhes sobre outras anotações suportadas pelo módulo.

Os exemplos seguintes mostram uma forma de modelar as tabelas Singers e Albums para o Spring Data Spanner:

  • Para entidades Singer, o exemplo inclui uma propriedade albums, com uma anotação @Interleaved. Esta propriedade contém uma lista de álbuns que estão intercalados com a entidade Singer. O Spring Data Spanner preenche esta propriedade automaticamente.
  • Para entidades Album, o exemplo inclui uma propriedade relatedAlbums que não está armazenada no Spanner.
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;
  }
}

Consultar e modificar dados

Para consultar e modificar dados com o Spring Data Spanner, pode adquirir um bean SpannerTemplate, que implementa SpannerOperations. SpannerTemplate oferece métodos para executar consultas SQL e modificar dados com declarações de linguagem de manipulação de dados (DML). Também pode usar este bean para aceder à API de leitura e à API de mutação para o Spanner.

Além disso, pode expandir a interface SpannerRepository para encapsular toda a lógica da aplicação que consulta e modifica dados no Spanner.

As secções seguintes explicam como trabalhar com SpannerTemplate e SpannerRepository.

Adquira um bean de modelo

Use a anotação @Autowired para adquirir automaticamente um bean SpannerTemplate. Em seguida, pode usar o SpannerTemplate durante a aula.

O exemplo seguinte mostra uma classe que adquire e usa o 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));
  }

}

Pode usar o SpannerTemplate bean para executar transações de apenas leitura e transações de leitura/escrita. Além disso, pode usar a anotação @Transactional para criar transações declarativas.

Adquira um bean de repositório

Se usar um SpannerRepository, pode usar a anotação @Autowired para adquirir um bean que implemente a interface do seu repositório. Um repositório inclui métodos para executar funções Java como transações só de leitura e transações de leitura/escrita. Para operações de nível inferior, pode obter o bean de modelo que o repositório usa.

Os exemplos seguintes mostram a interface de um repositório e uma classe que adquire e usa o 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");
  }

}

Faça a gestão do Spanner

Para obter informações sobre as suas bases de dados do Spanner, atualizar um esquema com uma declaração de linguagem de definição de dados (DDL) ou concluir outras tarefas administrativas, pode adquirir um bean SpannerDatabaseAdminTemplate.

Use a anotação @Autowired para adquirir o bean automaticamente. Em seguida, pode usar o SpannerDatabaseAdminTemplate durante a aula.

O exemplo seguinte mostra uma classe que adquire e usa o 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);
    }
  }
}

O que se segue?