Associe o JDBC a uma base de dados com dialeto GoogleSQL

Esta página mostra-lhe como realizar operações básicas no Spanner através do controlador JDBC do Spanner.

Instale o controlador JDBC

Siga os passos nas bibliotecas de cliente do Spanner para configurar a autenticação e, de seguida, adicione as dependências do controlador JDBC do Spanner, apresentadas no fragmento seguinte, ao seu ficheiro pom.xml.

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>com.google.cloud</groupId>
      <artifactId>libraries-bom</artifactId>
      <version>26.69.0</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

<dependencies>
  <dependency>
    <groupId>com.google.cloud</groupId>
    <artifactId>google-cloud-spanner-jdbc</artifactId>
    <exclusions>
      <exclusion>
        <groupId>com.google.api.grpc</groupId>
        <artifactId>proto-google-cloud-spanner-executor-v1</artifactId>
      </exclusion>
    </exclusions>
  </dependency>
Caso esteja a usar uma framework que exija o nome da classe Java para carregar o controlador JDBC, é com.google.cloud.spanner.jdbc.JdbcDriver. Consulte a documentação da API para JdbcDriver para saber como configurar uma ligação.

Associe a uma base de dados do Spanner

A descrição da classe JdbcDriver mostra a sintaxe da string de ligação e inclui um exemplo de código para criar uma ligação e executar uma consulta.

O controlador deteta automaticamente o dialeto SQL (GoogleSQL ou PostgreSQL) da base de dados especificada. Não é necessário nem permitido um parâmetro de dialeto.

Estabeleça ligação ao emulador

Para se ligar ao emulador, defina a variável de ambiente SPANNER_EMULATOR_HOST, por exemplo:

Linux / macOS

export SPANNER_EMULATOR_HOST=localhost:9010

Windows

set SPANNER_EMULATOR_HOST=localhost:9010

Isto indica ao controlador JDBC do Spanner que se ligue ao emulador em execução no localhost em vez do serviço de produção predefinido.

Exemplos

Os exemplos de código seguintes abrangem alguns exemplos de utilização comuns.

Execute uma atualização do esquema

O exemplo de código seguinte adiciona a tabela Singers à base de dados. Primeiro, cria uma ligação JDBC e, em seguida, cria a tabela:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

class CreateTableExample {

  static void createTable() throws SQLException {
    // TODO(developer): Replace these variables before running the sample.
    String projectId = "my-project";
    String instanceId = "my-instance";
    String databaseId = "my-database";
    createTable(projectId, instanceId, databaseId);
  }

  static void createTable(String projectId, String instanceId, String databaseId)
      throws SQLException {
    String connectionUrl =
        String.format(
            "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s",
            projectId, instanceId, databaseId);
    try (Connection connection = DriverManager.getConnection(connectionUrl)) {
      try (Statement statement = connection.createStatement()) {
        statement.execute(
            "CREATE TABLE Singers (\n"
                + "  SingerId   INT64 NOT NULL,\n"
                + "  FirstName  STRING(1024),\n"
                + "  LastName   STRING(1024),\n"
                + "  SingerInfo BYTES(MAX),\n"
                + "  Revenues   NUMERIC,\n"
                + ") PRIMARY KEY (SingerId)\n");
      }
    }
    System.out.println("Created table [Singers]");
  }
}

Use uma transação no modo de confirmação automática para adicionar linhas

Se não precisar de confirmar várias operações como um grupo, pode usar uma transação no modo de confirmação automática, que é o comportamento predefinido. O exemplo de código seguinte usa uma transação no modo de confirmação automática para adicionar linhas à tabela Singers:

import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Arrays;
import java.util.List;

class InsertDataExample {
  // Class to contain singer sample data.
  static class Singer {
    final long singerId;
    final String firstName;
    final String lastName;
    final BigDecimal revenues;

    Singer(long singerId, String firstName, String lastName, BigDecimal revenues) {
      this.singerId = singerId;
      this.firstName = firstName;
      this.lastName = lastName;
      this.revenues = revenues;
    }
  }

  static final List<Singer> SINGERS =
      Arrays.asList(
          new Singer(10, "Marc", "Richards", new BigDecimal("104100.00")),
          new Singer(20, "Catalina", "Smith", new BigDecimal("9880.99")),
          new Singer(30, "Alice", "Trentor", new BigDecimal("300183")),
          new Singer(40, "Lea", "Martin", new BigDecimal("20118.12")),
          new Singer(50, "David", "Lomond", new BigDecimal("311399.26")));

  static void insertData() throws SQLException {
    // TODO(developer): Replace these variables before running the sample.
    String projectId = "my-project";
    String instanceId = "my-instance";
    String databaseId = "my-database";
    insertData(projectId, instanceId, databaseId);
  }

  static void insertData(String projectId, String instanceId, String databaseId)
      throws SQLException {
    String connectionUrl =
        String.format(
            "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s",
            projectId, instanceId, databaseId);
    try (Connection connection = DriverManager.getConnection(connectionUrl)) {
      try (PreparedStatement ps =
          connection.prepareStatement(
              "INSERT INTO Singers\n"
                  + "(SingerId, FirstName, LastName, SingerInfo, Revenues)\n"
                  + "VALUES\n"
                  + "(?, ?, ?, ?, ?)")) {
        for (Singer singer : SINGERS) {
          ps.setLong(1, singer.singerId);
          ps.setString(2, singer.firstName);
          ps.setString(3, singer.lastName);
          ps.setNull(4, Types.BINARY);
          ps.setBigDecimal(5, singer.revenues);
          ps.addBatch();
        }
        int[] updateCounts = ps.executeBatch();
        System.out.printf("Insert counts: %s%n", Arrays.toString(updateCounts));
      }
    }
  }
}

Controle a forma como várias operações são confirmadas como um grupo

Se quiser controlar se o Spanner confirma várias operações em conjunto como um grupo, pode desativar o modo de confirmação automática. O exemplo de código seguinte usa connection.setAutoCommit(false) e connection.commit() para adicionar linhas à tabela Singers.

import com.google.common.collect.ImmutableList;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Arrays;

class BatchDmlExample {
  static class Singer {
    final long singerId;
    final String firstName;
    final String lastName;
    final BigDecimal revenues;

    Singer(long singerId, String firstName, String lastName, BigDecimal revenues) {
      this.singerId = singerId;
      this.firstName = firstName;
      this.lastName = lastName;
      this.revenues = revenues;
    }
  }

  static void batchDml() throws SQLException {
    // TODO(developer): Replace these variables before running the sample.
    String projectId = "my-project";
    String instanceId = "my-instance";
    String databaseId = "my-database";
    batchDml(projectId, instanceId, databaseId);
  }

  // This example shows how to execute a batch of DML statements with the JDBC driver.
  static void batchDml(String projectId, String instanceId, String databaseId) throws SQLException {
    String connectionUrl =
        String.format(
            "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s",
            projectId, instanceId, databaseId);

    ImmutableList<Singer> singers = ImmutableList.of(
        new Singer(10, "Marc", "Richards", BigDecimal.valueOf(10000)),
        new Singer(11, "Amirah", "Finney", BigDecimal.valueOf(195944.10d)),
        new Singer(12, "Reece", "Dunn", BigDecimal.valueOf(10449.90))
    );

    try (Connection connection = DriverManager.getConnection(connectionUrl)) {
      connection.setAutoCommit(false);
      // Use prepared statements for the lowest possible latency when executing the same SQL string
      // multiple times.
      try (PreparedStatement statement = connection.prepareStatement(
          "INSERT INTO Singers (SingerId, FirstName, LastName, Revenues)\n" 
              + "VALUES (?, ?, ?, ?)")) {
        for (Singer singer : singers) {
          statement.setLong(1, singer.singerId);
          statement.setString(2, singer.firstName);
          statement.setString(3, singer.lastName);
          statement.setBigDecimal(4, singer.revenues);
          // Add the current parameter values to the batch.
          statement.addBatch();
        }
        // Execute the batched statements.
        int[] updateCounts = statement.executeBatch();
        connection.commit();
        System.out.printf("Batch insert counts: %s%n", Arrays.toString(updateCounts));
      }
    }
  }
}

Execute uma consulta SQL

O exemplo de código seguinte devolve todas as linhas na tabela Singers ordenadas pelo apelido do cantor:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class SingleUseReadOnlyExample {

  static void singleUseReadOnly() throws SQLException {
    // TODO(developer): Replace these variables before running the sample.
    String projectId = "my-project";
    String instanceId = "my-instance";
    String databaseId = "my-database";
    singleUseReadOnly(projectId, instanceId, databaseId);
  }

  static void singleUseReadOnly(String projectId, String instanceId, String databaseId)
      throws SQLException {
    String connectionUrl =
        String.format(
            "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s",
            projectId, instanceId, databaseId);
    try (Connection connection = DriverManager.getConnection(connectionUrl);
        Statement statement = connection.createStatement()) {
      // When the connection is in autocommit mode, any query that is executed will automatically
      // be executed using a single-use read-only transaction, even if the connection itself is in
      // read/write mode.
      try (ResultSet rs =
          statement.executeQuery(
              "SELECT SingerId, FirstName, LastName, Revenues FROM Singers ORDER BY LastName")) {
        while (rs.next()) {
          System.out.printf(
              "%d %s %s %s%n",
              rs.getLong(1), rs.getString(2), rs.getString(3), rs.getBigDecimal(4));
        }
      }
    }
  }
}

O que se segue?