Primeros pasos con Cloud Spanner en Java

Primeros pasos con Cloud Spanner en Java

Objetivos

Este tutorial te guiará por los siguientes pasos que se deben realizar en la biblioteca cliente de Cloud Spanner para Java:

  • Crear una instancia y una base de datos de Cloud Spanner.
  • Escribir, leer y ejecutar consultas SQL sobre datos en la base de datos.
  • Actualizar el esquema de la base de datos.
  • Actualizar datos mediante una transacción de lectura y escritura.
  • Agregar un índice secundario a la base de datos.
  • Usar el índice para leer los datos y ejecutar consultas SQL sobre ellos.
  • Recuperar datos mediante una transacción de solo lectura.

Precios

Este tutorial usa Cloud Spanner, que es un componente facturable de Google Cloud Platform. Para obtener información sobre el coste de uso de Cloud Spanner, consulta el apartado sobre precios.

Antes de empezar

  1. Completa los pasos descritos en el apartado sobre configuración, en el que se explican la creación y la configuración de un proyecto predeterminado de Google Cloud Platform, en el que también se habilitan la facturación y la API de Cloud Spanner y se configura OAuth 2.0 con el fin de obtener credenciales de autenticación para usar la API de Cloud Spanner.
    En concreto, asegúrate de que ejecutas gcloud auth application-default login para configurar el entorno de desarrollo local con credenciales de autenticación.

  2. Instala los elementos indicados a continuación en la máquina de desarrollo si aún no están instalados:

  3. Clona el repositorio de aplicaciones de muestra en la máquina local:

    git clone https://github.com/GoogleCloudPlatform/java-docs-samples.git
    

    También puedes descargar la muestra como un archivo en formato zip y extraerla.

  4. Cambia al directorio que contiene el código de ejemplo de Cloud Spanner:

    cd java-docs-samples/spanner/cloud-client
    

Crear una instancia

Cuando usas Cloud Spanner por primera vez, debes crear una instancia, que es una asignación de recursos que usan las bases de datos de Cloud Spanner. Cuando creas una instancia, tienes que elegir una configuración de instancia, que determina dónde se almacenan tus datos y la cantidad de nodos que se van a usar, lo que permite conocer la cantidad de recursos de almacenamiento y publicación de la instancia.

Ejecuta el siguiente comando para crear una instancia de Cloud Spanner en la región us-central1 con un nodo:

gcloud spanner instances create test-instance --config=regional-us-central1 \
  --description="Test Instance" --nodes=1

Ten en cuenta que de este modo se crea una instancia con las siguientes características:

  • ID de la instancia test-instance
  • Nombre visible Test Instance
  • Configuración de instancia regional-us-central1 (Las configuraciones regionales almacenan datos en una región, mientras que las configuraciones de varias regiones distribuyen datos entre varias regiones. Más información en el apartado de instancias)
  • Recuento de nodo de 1 (node_count corresponde a la cantidad de recursos de almacenamiento y servicio disponibles para las bases de datos de la instancia. Más información en el apartado sobre recuento de nodos)

Deberías ver lo siguiente:

Creating instance...done.

Búsqueda en archivos de muestra

El repositorio de muestras contiene una en la que se explica cómo usar Cloud Spanner con Java.

El pom.xml agrega la biblioteca cliente de Cloud Spanner para Java a las dependencias del proyecto y configura el complemento de montaje para crear un archivo JAR ejecutable con la clase Java definida en este tutorial.

Crea la aplicación de ejemplo desde el directorio spanner/cloud-client:

mvn package
Echa un vistazo al archivo SpannerSample.java, que muestra cómo usar Cloud Spanner. El código muestra cómo crear y usar una nueva base de datos. Los datos usan el esquema de ejemplo que se muestra en la página sobre el esquema y el modelo de datos.

Crear una base de datos

Crea una base de datos llamada example-db en la instancia test-instance al ejecutar esto en la línea de comandos.

java -jar target/spanner-google-cloud-samples-1.0.8-jar-with-dependencies.jar \
createdatabase test-instance example-db

Deberías ver lo siguiente:

Created database [example-db]

Acabas de crear una base de datos de Cloud Spanner. Este es el código que creó la base de datos.

static void createDatabase(DatabaseAdminClient dbAdminClient, DatabaseId id) {
  Operation<Database, CreateDatabaseMetadata> op = dbAdminClient
      .createDatabase(
          id.getInstanceId().getInstance(),
          id.getDatabase(),
          Arrays.asList(
              "CREATE TABLE Singers (\n"
                  + "  SingerId   INT64 NOT NULL,\n"
                  + "  FirstName  STRING(1024),\n"
                  + "  LastName   STRING(1024),\n"
                  + "  SingerInfo BYTES(MAX)\n"
                  + ") PRIMARY KEY (SingerId)",
              "CREATE TABLE Albums (\n"
                  + "  SingerId     INT64 NOT NULL,\n"
                  + "  AlbumId      INT64 NOT NULL,\n"
                  + "  AlbumTitle   STRING(MAX)\n"
                  + ") PRIMARY KEY (SingerId, AlbumId),\n"
                  + "  INTERLEAVE IN PARENT Singers ON DELETE CASCADE"));
  Database db = op.waitFor().getResult();
  System.out.println("Created database [" + db.getId() + "]");
}

El código también define dos tablas, Singers y Albums, con las que se realiza una aplicación de música básica. Estas tablas se usan a lo largo de esta página. Echa un vistazo al esquema de ejemplo si aún no lo has hecho.

El siguiente paso consiste en escribir datos en la base de datos.

Crear un cliente de base de datos

Antes de que puedas leer o escribir, debes crear un DatabaseClient. Puedes pensar en el DatabaseClient como si fuera una conexión de base de datos: todas las interacciones con Cloud Spanner deben pasar por un DatabaseClient. Normalmente, creas un DatabaseClient cuando se inicia tu aplicación, y luego reutilizas ese DatabaseClient para leer, escribir y ejecutar transacciones.

SpannerOptions options = SpannerOptions.newBuilder().build();
Spanner spanner = options.getService();
try {
  String command = args[0];
  DatabaseId db = DatabaseId.of(options.getProjectId(), args[1], args[2]);
  DatabaseClient dbClient = spanner.getDatabaseClient(db);
  DatabaseAdminClient dbAdminClient = spanner.getDatabaseAdminClient();

Cada cliente usa recursos en Cloud Spanner, por lo que es una buena práctica cerrar clientes innecesarios llamando a close().

Lee más en la referencia de Javadoc de DatabaseClient.

Escribir datos

Los datos se escriben mediante objetos de Mutation. Un objeto de Mutation consiste en un contenedor para operaciones de mutación. Una Mutation representa una secuencia de inserciones, actualizaciones, eliminaciones, etc., que se pueden aplicar de forma atómica a diferentes filas y tablas en una base de datos de Cloud Spanner.

El método newInsertBuilder() en la clase de Mutation construye una mutación INSERT, que inserta una nueva fila en una tabla. Si ya existe la fila, no se puede escribir. También puedes usar el método newInsertOrUpdateBuilder() para construir una mutación INSERT_OR_UPDATE, que actualiza los valores de la columna si ya existe la fila.

El método write() de la clase DatabaseClient escribe las mutaciones. Todas las mutaciones de un solo lote se aplican de forma atómica.

Este código muestra cómo escribir los datos:

static final List<Singer> SINGERS =
    Arrays.asList(
        new Singer(1, "Marc", "Richards"),
        new Singer(2, "Catalina", "Smith"),
        new Singer(3, "Alice", "Trentor"),
        new Singer(4, "Lea", "Martin"),
        new Singer(5, "David", "Lomond"));

static final List<Album> ALBUMS =
    Arrays.asList(
        new Album(1, 1, "Total Junk"),
        new Album(1, 2, "Go, Go, Go"),
        new Album(2, 1, "Green"),
        new Album(2, 2, "Forever Hold Your Peace"),
        new Album(2, 3, "Terrified"));
static void writeExampleData(DatabaseClient dbClient) {
  List<Mutation> mutations = new ArrayList<>();
  for (Singer singer : SINGERS) {
    mutations.add(
        Mutation.newInsertBuilder("Singers")
            .set("SingerId")
            .to(singer.singerId)
            .set("FirstName")
            .to(singer.firstName)
            .set("LastName")
            .to(singer.lastName)
            .build());
  }
  for (Album album : ALBUMS) {
    mutations.add(
        Mutation.newInsertBuilder("Albums")
            .set("SingerId")
            .to(album.singerId)
            .set("AlbumId")
            .to(album.albumId)
            .set("AlbumTitle")
            .to(album.albumTitle)
            .build());
  }
  dbClient.write(mutations);
}

(Si deseas más detalles sobre los datos, consulta el esquema de ejemplo sobre tablas Singers y Albums).

Ejecuta la muestra mediante el argumento write.

java -jar target/spanner-google-cloud-samples-1.0.8-jar-with-dependencies.jar \
write test-instance example-db

Consultar datos mediante SQL

Cloud Spanner es compatible con una interfaz SQL nativa de lectura de datos, a la que se puede acceder desde la línea de comandos mediante la herramienta de línea de comandos gcloud o mediante programación, a través de la biblioteca cliente de Cloud Spanner para Java.

En la línea de comandos

Ejecuta la siguiente instrucción SQL para leer los valores de todas las columnas de la tabla Albums:

gcloud spanner databases execute-sql example-db --instance=test-instance --sql='SELECT SingerId, AlbumId, AlbumTitle FROM Albums'

El resultado debería ser el siguiente:

SingerId AlbumId AlbumTitle
1        1       Total Junk
1        2       Go, Go, Go
2        1       Green
2        2       Forever Hold Your Peace
2        3       Terrified

Uso de la biblioteca cliente de Cloud Spanner para Java

Además de ejecutar una instrucción SQL en la línea de comandos, puedes emitir la misma instrucción SQL mediante programación utilizando la biblioteca cliente de Cloud Spanner para Java.

Los siguientes métodos y clases sirven para ejecutar la consulta SQL:

  • El método singleUse() de la clase DatabaseClient: sirve para leer el valor de una o más columnas de una o más filas en una tabla de Cloud Spanner. singleUse() devuelve un objeto ReadContext, que se utiliza para ejecutar una instrucción de lectura o SQL.
  • El método executeQuery() de la clase ReadContext: sirve para ejecutar una consulta en una base de datos.
  • La clase Statement: sirve para crear una cadena SQL.
  • La clase ResultSet: se emplea para acceder a los datos devueltos por una instrucción SQL o llamada de lectura.

A continuación, se indica cómo emitir la consulta y acceder a los datos:

static void query(DatabaseClient dbClient) {
  // singleUse() can be used to execute a single read or query against Cloud Spanner.
  ResultSet resultSet =
      dbClient
          .singleUse()
          .executeQuery(Statement.of("SELECT SingerId, AlbumId, AlbumTitle FROM Albums"));
  while (resultSet.next()) {
    System.out.printf(
        "%d %d %s\n", resultSet.getLong(0), resultSet.getLong(1), resultSet.getString(2));
  }
}

Ejecuta la muestra mediante el argumento query.

java -jar target/spanner-google-cloud-samples-1.0.8-jar-with-dependencies.jar \
query test-instance example-db

El resultado debe ser el siguiente:

1 1 Total Junk
1 2 Go, Go, Go
2 1 Green
2 2 Forever Hold Your Peace
2 3 Terrified

Leer datos mediante la API de lectura

Además de la interfaz SQL de Cloud Spanner, Cloud Spanner también es compatible con una interfaz de lectura.

Usa el método read() de la clase ReadContext para leer las filas de la base de datos. Usa un objeto KeySet para definir una colección de claves e intervalos de claves que leer.

Así se deben leer los datos:

static void read(DatabaseClient dbClient) {
  ResultSet resultSet =
      dbClient
          .singleUse()
          .read("Albums",
              // KeySet.all() can be used to read all rows in a table. KeySet exposes other
              // methods to read only a subset of the table.
              KeySet.all(),
              Arrays.asList("SingerId", "AlbumId", "AlbumTitle"));
  while (resultSet.next()) {
    System.out.printf(
        "%d %d %s\n", resultSet.getLong(0), resultSet.getLong(1), resultSet.getString(2));
  }
}

Ejecuta la muestra mediante el argumento read.

java -jar target/spanner-google-cloud-samples-1.0.8-jar-with-dependencies.jar \
read test-instance example-db

El resultado que verás debe parecerse al siguiente:

1 1 Total Junk
1 2 Go, Go, Go
2 1 Green
2 2 Forever Hold Your Peace
2 3 Terrified

Actualizar el esquema de la base de datos

Supongamos que necesitas añadir una nueva columna llamada MarketingBudget a la tabla Albums. Para agregar una nueva columna a una tabla existente, es preciso actualizar el esquema de base de datos. Cloud Spanner es compatible con actualizaciones de esquema de bases de datos mientras que dicha base de datos sigue atendiendo el tráfico. Las actualizaciones de esquema no requieren que la base de datos esté desconectada ni bloquean tablas ni columnas enteras; puedes continuar escribiendo datos en la base de datos durante la actualización del esquema. Obtén más información sobre las actualizaciones de esquema compatibles y el rendimiento de cambio de esquema en el apartado sobre actualización de esquemas.

Añadir una columna

Puedes añadir una columna en la línea de comandos mediante la herramienta de línea de comandos gcloud o a través de la biblioteca del cliente de Cloud Spanner para Java.

En la línea de comandos

Usa el siguiente comando ALTER TABLE para añadir la nueva columna a la tabla:

gcloud spanner databases ddl update example-db --instance=test-instance \
  --ddl='ALTER TABLE Albums ADD COLUMN MarketingBudget INT64'

Deberías ver lo siguiente:

DDL updating...done.

Uso de la biblioteca cliente de Cloud Spanner para Java

Usa el método updateDatabaseDdl() de la clase DatabaseAdminClient para modificar el esquema:

static void addMarketingBudget(DatabaseAdminClient adminClient, DatabaseId dbId) {
  adminClient.updateDatabaseDdl(dbId.getInstanceId().getInstance(),
      dbId.getDatabase(),
      Arrays.asList("ALTER TABLE Albums ADD COLUMN MarketingBudget INT64"),
      null).waitFor();
  System.out.println("Added MarketingBudget column");
}

Ejecuta la muestra mediante el argumento addmarketingbudget.

java -jar target/spanner-google-cloud-samples-1.0.8-jar-with-dependencies.jar \
addmarketingbudget test-instance example-db

Deberías ver lo siguiente:

Added MarketingBudget column.

Escribir datos en la nueva columna

El siguiente código sirve para escribir datos en la nueva columna. Establece MarketingBudget en 100000 en la fila marcada por Albums(1, 1), y 500000 en la fila marcada por Albums(2, 2).

static void update(DatabaseClient dbClient) {
  // Mutation can be used to update/insert/delete a single row in a table. Here we use
  // newUpdateBuilder to create update mutations.
  List<Mutation> mutations =
      Arrays.asList(
          Mutation.newUpdateBuilder("Albums")
              .set("SingerId")
              .to(1)
              .set("AlbumId")
              .to(1)
              .set("MarketingBudget")
              .to(100000)
              .build(),
          Mutation.newUpdateBuilder("Albums")
              .set("SingerId")
              .to(2)
              .set("AlbumId")
              .to(2)
              .set("MarketingBudget")
              .to(500000)
              .build());
  // This writes all the mutations to Cloud Spanner atomically.
  dbClient.write(mutations);
}

Ejecuta la muestra mediante el argumento update.

java -jar target/spanner-google-cloud-samples-1.0.8-jar-with-dependencies.jar \
update test-instance example-db

También puedes ejecutar una consulta SQL o una llamada de lectura para recuperar los valores que acabas de escribir.

Aquí está el código para ejecutar la consulta:

static void queryMarketingBudget(DatabaseClient dbClient) {
  // Rows without an explicit value for MarketingBudget will have a MarketingBudget equal to
  // null.
  ResultSet resultSet =
      dbClient
          .singleUse()
          .executeQuery(Statement.of("SELECT SingerId, AlbumId, MarketingBudget FROM Albums"));
  while (resultSet.next()) {
    System.out.printf(
        "%d %d %s\n",
        resultSet.getLong("SingerId"),
        resultSet.getLong("AlbumId"),
        // We check that the value is non null. ResultSet getters can only be used to retrieve
        // non null values.
        resultSet.isNull("MarketingBudget") ? "NULL" : resultSet.getLong("MarketingBudget"));
  }
}

Si quieres ejecutar esta consulta, ejecuta la muestra con el argumento querymarketingbudget.

java -jar target/spanner-google-cloud-samples-1.0.8-jar-with-dependencies.jar \
querymarketingbudget test-instance example-db

Deberías ver lo siguiente:

1 1 100000
1 2 NULL
2 1 NULL
2 2 500000
2 3 NULL

Actualizar datos mediante una transacción de lectura y escritura

Supongamos que las ventas de Albums(1, 1) son más bajas de lo esperado. Por este motivo, deseas mover 200.000 USD del presupuesto de comercialización de Albums(2, 2) a Albums(1, 1), pero solo si el presupuesto de Albums(2, 2) es de, al menos, 300.000 USD.

Debido a que esta transacción puede escribir datos que difieren según los valores que se leen, tienes que usar una transacción de lectura y escritura para realizar las lecturas y escrituras de forma automática.

Usa la interfaz TransactionRunner para ejecutar un cuerpo de trabajo en el contexto de una transacción de lectura y escritura. Esta interfaz incluye el método run(), que sirve para ejecutar una transacción de lectura y escritura con todos los intentos que sean necesarios. El método readWriteTransaction() de la clase DatabaseClient devuelve un objeto TransactionRunner para ejecutar una única transacción lógica.

La clase TransactionRunner.TransactionCallable incluye un método run() para llevar a cabo un solo intento de una transacción. run() toma un objeto TransactionContext, que constituye un contexto para una transacción.

La muestra utiliza la clase Struct, que sirve para almacenar los resultados de las llamadas a readRow(). El ejemplo también usa la clase Key, que representa una clave de registro en una tabla o índice de Cloud Spanner.

Este es el código para ejecutar la transacción:

static void writeWithTransaction(DatabaseClient dbClient) {
  dbClient
      .readWriteTransaction()
      .run(
          new TransactionCallable<Void>() {
            @Override
            public Void run(TransactionContext transaction) throws Exception {
              // Transfer marketing budget from one album to another. We do it in a transaction to
              // ensure that the transfer is atomic.
              Struct row =
                  transaction.readRow("Albums", Key.of(2, 2), Arrays.asList("MarketingBudget"));
              long album2Budget = row.getLong(0);
              // Transaction will only be committed if this condition still holds at the time of
              // commit. Otherwise it will be aborted and the callable will be rerun by the
              // client library.
              if (album2Budget >= 300000) {
                long album1Budget =
                    transaction
                        .readRow("Albums", Key.of(1, 1), Arrays.asList("MarketingBudget"))
                        .getLong(0);
                long transfer = 200000;
                album1Budget += transfer;
                album2Budget -= transfer;
                transaction.buffer(
                    Mutation.newUpdateBuilder("Albums")
                        .set("SingerId")
                        .to(1)
                        .set("AlbumId")
                        .to(1)
                        .set("MarketingBudget")
                        .to(album1Budget)
                        .build());
                transaction.buffer(
                    Mutation.newUpdateBuilder("Albums")
                        .set("SingerId")
                        .to(2)
                        .set("AlbumId")
                        .to(2)
                        .set("MarketingBudget")
                        .to(album2Budget)
                        .build());
              }
              return null;
            }
          });
}

Ejecuta la muestra mediante el argumento writetransaction.

java -jar target/spanner-google-cloud-samples-1.0.8-jar-with-dependencies.jar \
writetransaction test-instance example-db

Consulta los datos de nuevo:

java -jar target/spanner-google-cloud-samples-1.0.8-jar-with-dependencies.jar \
querymarketingbudget test-instance example-db

Debería ver que 200.000 USD se transfirieron con éxito desde Albums(2, 2) a Albums(1, 1):

1 1 300000
1 2 NULL
2 1 NULL
2 2 300000
2 3 NULL

Usar un índice secundario

Supongamos que quieres buscar todas las filas de Albums que tienen valores de AlbumTitle en un intervalo determinado. Puedes leer todos los valores de la columna AlbumTitle mediante una instrucción SQL o una lectura y descartar después las filas que no cumplan los criterios, pero realizar esta búsqueda completa en la tabla resulta caro, especialmente en el caso de tablas con muchas filas. En su lugar, puedes acelerar la recuperación de filas cuando busques columnas de clave no principales mediante la creación de un índice secundario en la tabla.

Para añadir un índice secundario a una tabla existente, es preciso actualizar el esquema. Al igual que otras actualizaciones de esquema, Cloud Spanner permite añadir un índice mientras la base de datos continúa atendiendo el tráfico. Cloud Spanner rellena el índice con datos (también conocidos como "reposiciones") desde dentro. Las reposiciones pueden tardar unos minutos en completarse, pero no es necesario desconectar la base de datos ni evitar escribir en ciertas tablas o columnas durante este proceso. Para más detalles, consulta el apartado sobre reposiciones de índice.

Añadir un índice secundario

Puede añadir un índice en la línea de comandos mediante la herramienta de línea de comandos de gcloud o mediante programación a través de la biblioteca cliente de Cloud Spanner para Java.

En la línea de comandos

Usa el siguiente comando CREATE INDEX para agregar un índice a la base de datos:

gcloud spanner databases ddl update example-db --instance=test-instance \
  --ddl='CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle)'

Deberías ver lo siguiente:

DDL updating...done.

Uso de la biblioteca cliente de Cloud Spanner para Java

Usa el método updateDatabaseDdl() de la clase DatabaseAdminClient para añadir un índice:

static void addIndex(DatabaseAdminClient adminClient, DatabaseId dbId) {
  adminClient.updateDatabaseDdl(dbId.getInstanceId().getInstance(),
      dbId.getDatabase(),
      Arrays.asList("CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle)"),
      null).waitFor();
  System.out.println("Added AlbumsByAlbumTitle index");
}

Ejecuta la muestra mediante el argumento addindex.

java -jar target/spanner-google-cloud-samples-1.0.8-jar-with-dependencies.jar \
addindex test-instance example-db

Añadir un índice es un proceso que puede llevar unos minutos. Esto es lo que debes ver después de añadir el índice:

Added the AlbumsByAlbumTitle index.

Consultas mediante el índice

Puedes realizar consultas mediante el nuevo índice en la línea de comandos o mediante la biblioteca cliente.

En la línea de comandos

Ejecuta una instrucción SQL con la herramienta de línea de comandos gcloud para buscar AlbumId, AlbumTitle y MarketingBudget en Albums mediante el índice AlbumsByAlbumTitle con el intervalo de AlbumsTitle en ["Aardvark", "Goo").

gcloud spanner databases execute-sql example-db --instance=test-instance --sql='SELECT AlbumId, AlbumTitle, MarketingBudget FROM Albums@{FORCE_INDEX=AlbumsByAlbumTitle} WHERE AlbumTitle >= "Aardvark" AND AlbumTitle < "Goo"'

El resultado debería ser el siguiente:

AlbumId  AlbumTitle               MarketingBudget
2        Go, Go, Go
2        Forever Hold Your Peace  300000

Uso de la biblioteca cliente de Cloud Spanner para Java

El código para usar el índice mediante programación es similar al código de consulta utilizado anteriormente.

static void queryUsingIndex(DatabaseClient dbClient) {
  Statement statement = Statement
      // We use FORCE_INDEX hint to specify which index to use. For more details see
      // https://cloud.google.com/spanner/docs/query-syntax#from-clause
      .newBuilder("SELECT AlbumId, AlbumTitle, MarketingBudget\n"
          + "FROM Albums@{FORCE_INDEX=AlbumsByAlbumTitle}\n"
          + "WHERE AlbumTitle >= @StartTitle AND AlbumTitle < @EndTitle")
      // We use @BoundParameters to help speed up frequently executed queries.
      //  For more details see https://cloud.google.com/spanner/docs/sql-best-practices
      .bind("StartTitle").to("Aardvark")
      .bind("EndTitle").to("Goo")
      .build();

  ResultSet resultSet = dbClient.singleUse().executeQuery(statement);
  while (resultSet.next()) {
    System.out.printf(
        "%d %s %s\n",
        resultSet.getLong("AlbumId"),
        resultSet.getString("AlbumTitle"),
        resultSet.isNull("MarketingBudget") ? "NULL" : resultSet.getLong("MarketingBudget"));
  }
}

Ejecuta la muestra mediante el argumento queryindex.

java -jar target/spanner-google-cloud-samples-1.0.8-jar-with-dependencies.jar \
queryindex test-instance example-db

El resultado que verás debe parecerse al siguiente:

2 Go, Go, Go NULL
2 Forever Hold Your Peace 300000

Para más detalles, consulta la referencia de:

Leer mediante el índice

Para leer a través del índice, usa el método readUsingIndex() de la clase ReadContext.

El siguiente código captura todas las columnas de AlbumId y AlbumTitle del índice AlbumsByAlbumTitle.

static void readUsingIndex(DatabaseClient dbClient) {
  ResultSet resultSet =
      dbClient
          .singleUse()
          .readUsingIndex(
              "Albums",
              "AlbumsByAlbumTitle",
              KeySet.all(),
              Arrays.asList("AlbumId", "AlbumTitle"));
  while (resultSet.next()) {
    System.out.printf("%d %s\n", resultSet.getLong(0), resultSet.getString(1));
  }
}

Ejecuta la muestra mediante el argumento readindex.

java -jar target/spanner-google-cloud-samples-1.0.8-jar-with-dependencies.jar \
readindex test-instance example-db

Deberías ver lo siguiente:

2 Forever Hold Your Peace
2 Go, Go, Go
1 Green
3 Terrified
1 Total Junk

Agregar un índice con una cláusula STORING

Puede que hayas notado que el ejemplo de lectura anterior no incluye la lectura de la columna MarketingBudget. Esto se debe a que la interfaz de lectura de Cloud Spanner no admite la capacidad de unirse a un índice con una tabla de datos para buscar valores que no están almacenados en él.

Crea una definición alternativa de AlbumsByAlbumTitle que almacene una copia de MarketingBudget en el índice.

En la línea de comandos

gcloud spanner databases ddl update example-db --instance=test-instance \
  --ddl='CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle) STORING (MarketingBudget)'

Añadir un índice es un proceso que puede llevar unos minutos. Esto es lo que debes ver después de añadir el índice:

DDL updating...done.

Uso de la biblioteca cliente de Cloud Spanner para Java

Usa el método updateDatabaseDdl() de la clase DatabaseAdminClient para añadir un índice con una cláusula STORING:

static void addStoringIndex(DatabaseAdminClient adminClient, DatabaseId dbId) {
  adminClient.updateDatabaseDdl(dbId.getInstanceId().getInstance(),
      dbId.getDatabase(),
      Arrays.asList(
          "CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle) STORING (MarketingBudget)"),
      null).waitFor();
  System.out.println("Added AlbumsByAlbumTitle2 index");
}

Ejecuta la muestra mediante el argumento addstoringindex.

java -jar target/spanner-google-cloud-samples-1.0.8-jar-with-dependencies.jar \
addstoringindex test-instance example-db

Añadir un índice es un proceso que puede llevar unos minutos. Esto es lo que debes ver después de añadir el índice:

Added the AlbumsByAlbumTitle2 index.

Ahora puedes ejecutar una lectura que recopile las columnas AlbumId, AlbumTitle, y MarketingBudget del índice AlbumsByAlbumTitle2:

static void readStoringIndex(DatabaseClient dbClient) {
  // We can read MarketingBudget also from the index since it stores a copy of MarketingBudget.
  ResultSet resultSet =
      dbClient
          .singleUse()
          .readUsingIndex(
              "Albums",
              "AlbumsByAlbumTitle2",
              KeySet.all(),
              Arrays.asList("AlbumId", "AlbumTitle", "MarketingBudget"));
  while (resultSet.next()) {
    System.out.printf(
        "%d %s %s\n",
        resultSet.getLong(0),
        resultSet.getString(1),
        resultSet.isNull("MarketingBudget") ? "NULL" : resultSet.getLong("MarketingBudget"));
  }
}

Ejecuta la muestra mediante el argumento readstoringindex.

java -jar target/spanner-google-cloud-samples-1.0.8-jar-with-dependencies.jar \
readstoringindex test-instance example-db

El resultado que verás debe parecerse al siguiente:

2 Forever Hold Your Peace 300000
2 Go, Go, Go NULL
1 Green NULL
3 Terrified NULL
1 Total Junk 300000

Recuperar datos mediante transacciones de solo lectura

Supongamos que deseas ejecutar más de una lectura en la misma marca de tiempo. En las transacciones de solo lectura se observa un prefijo uniforme del historial de confirmación de transacción, por lo que la aplicación siempre obtiene datos uniformes. Usa un objeto ReadOnlyTransaction para ejecutar transacciones de solo lectura. Usa el método readOnlyTransaction() de la clase DatabaseClient para obtener un objeto ReadOnlyTransaction.

A continuación, se muestra cómo ejecutar una consulta y realizar una lectura en la misma transacción de solo lectura:

static void readOnlyTransaction(DatabaseClient dbClient) {
  // ReadOnlyTransaction must be closed by calling close() on it to release resources held by it.
  // We use a try-with-resource block to automatically do so.
  try (ReadOnlyTransaction transaction = dbClient.readOnlyTransaction()) {
    ResultSet queryResultSet =
        transaction.executeQuery(
            Statement.of("SELECT SingerId, AlbumId, AlbumTitle FROM Albums"));
    while (queryResultSet.next()) {
      System.out.printf(
          "%d %d %s\n",
          queryResultSet.getLong(0), queryResultSet.getLong(1), queryResultSet.getString(2));
    }
    ResultSet readResultSet =
        transaction.read(
            "Albums", KeySet.all(), Arrays.asList("SingerId", "AlbumId", "AlbumTitle"));
    while (readResultSet.next()) {
      System.out.printf(
          "%d %d %s\n",
          readResultSet.getLong(0), readResultSet.getLong(1), readResultSet.getString(2));
    }
  }
}

Ejecuta la muestra mediante el argumento readonlytransaction.

java -jar target/spanner-google-cloud-samples-1.0.8-jar-with-dependencies.jar \
readonlytransaction test-instance example-db

El resultado que verás debe parecerse al siguiente:

2 2 Forever Hold Your Peace
1 2 Go, Go, Go
2 1 Green
2 3 Terrified
1 1 Total Junk
1 1 Total Junk
1 2 Go, Go, Go
2 1 Green
2 2 Forever Hold Your Peace
2 3 Terrified

Limpieza

Si quieres evitar que se generen costes adicionales en la cuenta de Google Cloud Platform debido a los recursos usados en este tutorial, elimina la base de datos y la instancia que creaste.

Eliminar la base de datos

Al eliminar una instancia, se eliminan automáticamente todas sus bases de datos. En este paso se muestra cómo eliminar una base de datos sin eliminar una instancia (se seguirían generando costes por la instancia).

En la línea de comandos

gcloud spanner databases delete example-db --instance=test-instance

Mediante la consola de GCP

  1. Ve a la página de instancias de Cloud Spanner en la consola de Google Cloud Platform.
    Ir a la página de instancias de Cloud Spanner
  2. Haz clic en la instancia.
  3. Haz clic en la base de datos que quieras eliminar.
  4. En la página sobre detalles de la base de datos, haz clic en Eliminar.
  5. Confirma que deseas eliminar la base de datos y haz clic en Eliminar.

Eliminar la instancia

Al eliminar una instancia, se borran todas las bases de datos creadas en dicha instancia.

En la línea de comandos

gcloud spanner instances delete test-instance

Mediante la consola de GCP

  1. Ve a la página de instancias de Cloud Spanner en la consola de Google Cloud Platform.
    Ir a la página de instancias de Cloud Spanner
  2. Haz clic en tu instancia.
  3. Haz clic en Eliminar.
  4. Confirma que deseas eliminar la instancia y haz clic en Eliminar.

Siguientes pasos

¿Te sirvió esta página? Envíanos tu opinión:

Enviar comentarios sobre…

Cloud Spanner Documentation