Integrar o Spanner ao Spring Data

O módulo Spring Data Spanner ajuda você a usar o Spanner em qualquer aplicativo Java criado com o Spring Framework.

Como todos os módulos Spring Data, o Spring Data Spanner fornece um modelo de programação baseado em Spring que mantém as garantias de consistência e escalonabilidade do Spanner. Os recursos são semelhantes ao Spring Data JPA e ao Hibernate ORM, com anotações projetadas para o Spanner. Para mais informações sobre como usar o Spring Data JPA com o Spanner, consulte Integrar o Spanner ao Spring Data JPA (dialeto GoogleSQL).

Se você já está familiarizado com o Spring, o Spring Data Spanner pode facilitar o trabalho com o Spanner em seu aplicativo e reduzir a quantidade de código que você precisa escrever.

Nesta página, explicamos como adicionar o Spring Data Spanner a um aplicativo Java. Para informações detalhadas sobre o módulo, consulte a referência do Spring Data Spanner.

Instalar o módulo

Se você usa o Maven, adicione a Lista de materiais (BOM, na sigla em inglês) do GCP do Spring Cloud e o Spring Data Spanner ao seu arquivo pom.xml. Essas dependências fornecem os componentes do Spring Data Spanner ao 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 é necessário criar uma conta de serviço e usar a chave da conta de serviço para autenticar no Google Cloud.

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

Configurar o módulo

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

Especifique uma instância e um banco de dados

Para especificar a instância padrão e o banco de dados, defina as propriedades de configuração a seguir no seu aplicativo:

Propriedade Descrição
spring.cloud.gcp.spanner.project-id Opcional. O ID do projeto do Google Cloud. 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 O banco de dados para se conectar.

Dados do modelo do Spanner

Com o Spring Data Spanner, é possível usar objetos Java antigos simples (POJOs, na sigla em inglês) para modelar os dados armazenados nas tabelas do Spanner.

Para cada tabela no seu banco de dados, declare uma entidade que representa um registro nessa tabela. Use anotações para mapear a entidade e as propriedades dela para uma tabela e colunas.

Use as anotações abaixo para modelar relações simples entre entidades e tabelas:

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

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

Quando você omite essa propriedade, a estratégia de nomenclatura padrão para o Spring Data Spanner mapeia nomes de propriedades camelCase em Java para nomes de colunas PascalCase. Por exemplo, a propriedade singerId mapeia para o nome da coluna SingerId.

@Embedded

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

@Interleaved

@Interleaved(lazy = true)

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

Por padrão, o Spring Data Spanner busca as linhas intercaladas na criação da instância. Para buscar as linhas de maneira mais cômoda, quando acessar a propriedade, use @Interleaved(lazy = true).

Exemplo: se uma entidade Singer puder ter entradas Album intercaladas como filho, 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 no banco de dados e deve ser ignorada.

@PrimaryKey

@PrimaryKey(keyOrder = N)

Indica que a propriedade é um componente da chave primária e identifica a posição dessa propriedade na chave, começando em 1. O keyOrder padrão é 1.

Exemplo: @PrimaryKey(keyOrder = 3)

@Table(name = "TABLE_NAME")

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

Exemplo: @Table(name = "Singers")

Se você precisar modelar relacionamentos mais complexos, consulte a referência do Spring Data Spanner para detalhes sobre outras anotações compatíveis com o módulo.

Os exemplos a seguir mostram uma maneira 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. Essa propriedade contém uma lista de álbuns que são intercalados com a entidade Singer. O Spring Data Spanner preenche essa propriedade automaticamente.
  • Para entidades Album, o exemplo inclui uma propriedade relatedAlbums que não é 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, adquira um bean SpannerTemplate, que implementa SpannerOperations. SpannerTemplate fornece métodos para realizar consultas SQL e modificar dados com instruções de linguagem de manipulação de dados (DML). Também é possível usar esse bean para acessar a API de leitura e a API de mutação para o Spanner.

Além disso, é possível estender a interface SpannerRepository para encapsular toda a lógica do aplicativo que consulta e modifica dados no Spanner.

As seções a seguir explicam como trabalhar com SpannerTemplate e SpannerRepository.

Adquirir um bean de modelo

Use a anotação @Autowired para adquirir um bean SpannerTemplate automaticamente. Assim, é possível usar SpannerTemplate em toda a classe.

O exemplo a seguir 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));
  }

}

É possível usar o bean SpannerTemplate para executar transações somente leitura e transações de leitura e gravação. Além disso, é possível usar a anotação @Transactional para criar transações declarativas.

Adquirir um bean de repositório

Caso você use um SpannerRepository, poderá adotar a anotação @Autowired para adquirir um bean que implementa a interface do seu repositório. Um repositório inclui métodos para executar funções Java como transações somente leitura e transações de leitura e gravação. Para operações de nível inferior, é possível conseguir o bean de modelo que o repositório usa.

Os exemplos a seguir 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");
  }

}

Gerenciar o Spanner

Para receber informações sobre os bancos de dados do Spanner, atualizar um esquema com uma instrução de linguagem de definição de dados (DDL, na sigla em inglês) ou concluir outras tarefas administrativas, adquira um bean SpannerDatabaseAdminTemplate.

Use a anotação @Autowired para adquirir o bean automaticamente. Assim, é possível usar SpannerDatabaseAdminTemplate em toda a classe.

O exemplo a seguir 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);
    }
  }
}

A seguir