Como inserir, atualizar e excluir dados usando mutações

Nesta página, descrevemos como inserir, atualizar e excluir dados usando mutações. É possível confirmar mutações usando gRPC ou REST, mas é mais comum acessar as APIs por meio das bibliotecas de cliente.

Nesta página, mostramos as tarefas básicas de inserir, atualizar e excluir. É possível encontrar mais exemplos nos tutoriais de primeiros passos.

Inserir novas linhas em uma tabela

C#

É possível inserir dados usando o método connection.CreateInsertCommand(), que cria um novo SpannerCommand para inserir linhas em uma tabela. Com o método SpannerCommand.ExecuteNonQueryAsync(), novas linhas são adicionadas à tabela.

Este código mostra como inserir dados:

public class Singer
{
    public int singerId { get; set; }
    public string firstName { get; set; }
    public string lastName { get; set; }
}

public class Album
{
    public int singerId { get; set; }
    public int albumId { get; set; }
    public string albumTitle { get; set; }
}
public static async Task InsertSampleDataAsync(
    string projectId, string instanceId, string databaseId)
{
    const int firstSingerId = 1;
    const int secondSingerId = 2;
    string connectionString =
    $"Data Source=projects/{projectId}/instances/{instanceId}"
    + $"/databases/{databaseId}";
    List<Singer> singers = new List<Singer> {
        new Singer {singerId = firstSingerId, firstName = "Marc",
            lastName = "Richards"},
        new Singer {singerId = secondSingerId, firstName = "Catalina",
            lastName = "Smith"},
        new Singer {singerId = 3, firstName = "Alice",
            lastName = "Trentor"},
        new Singer {singerId = 4, firstName = "Lea",
            lastName = "Martin"},
        new Singer {singerId = 5, firstName = "David",
            lastName = "Lomond"},
    };
    List<Album> albums = new List<Album> {
        new Album {singerId = firstSingerId, albumId = 1,
            albumTitle = "Total Junk"},
        new Album {singerId = firstSingerId, albumId = 2,
            albumTitle = "Go, Go, Go"},
        new Album {singerId = secondSingerId, albumId = 1,
            albumTitle = "Green"},
        new Album {singerId = secondSingerId, albumId = 2,
            albumTitle = "Forever Hold your Peace"},
        new Album {singerId = secondSingerId, albumId = 3,
            albumTitle = "Terrified"},
    };
    // Create connection to Cloud Spanner.
    using (var connection = new SpannerConnection(connectionString))
    {
        await connection.OpenAsync();

        // Insert rows into the Singers table.
        var cmd = connection.CreateInsertCommand("Singers",
            new SpannerParameterCollection {
                {"SingerId", SpannerDbType.Int64},
                {"FirstName", SpannerDbType.String},
                {"LastName", SpannerDbType.String}
        });
        await Task.WhenAll(singers.Select(singer =>
        {
            cmd.Parameters["SingerId"].Value = singer.singerId;
            cmd.Parameters["FirstName"].Value = singer.firstName;
            cmd.Parameters["LastName"].Value = singer.lastName;
            return cmd.ExecuteNonQueryAsync();
        }));

        // Insert rows into the Albums table.
        cmd = connection.CreateInsertCommand("Albums",
            new SpannerParameterCollection {
                {"SingerId", SpannerDbType.Int64},
                {"AlbumId", SpannerDbType.Int64},
                {"AlbumTitle", SpannerDbType.String}
        });
        await Task.WhenAll(albums.Select(album =>
        {
            cmd.Parameters["SingerId"].Value = album.singerId;
            cmd.Parameters["AlbumId"].Value = album.albumId;
            cmd.Parameters["AlbumTitle"].Value = album.albumTitle;
            return cmd.ExecuteNonQueryAsync();
        }));
        Console.WriteLine("Inserted data.");
    }
}

Go

Grave os dados usando uma Mutation. Mutation é um contêiner para operações de mutação. Uma Mutation representa uma sequência de inserções, atualizações, exclusões e outras que podem ser aplicadas atomicamente em diferentes linhas e tabelas em um banco de dados do Cloud Spanner.

Use Mutation.InsertOrUpdate() para construir uma mutação INSERT_OR_UPDATE, que adiciona uma nova linha ou atualiza valores de coluna caso a linha já exista. Outra alternativa é usar o método Mutation.Insert() para construir uma mutação INSERT, que adiciona uma nova linha.

Client.Apply() aplica mutações atomicamente a um banco de dados.

Este código mostra como gravar os dados:

func write(ctx context.Context, w io.Writer, client *spanner.Client) error {
	singerColumns := []string{"SingerId", "FirstName", "LastName"}
	albumColumns := []string{"SingerId", "AlbumId", "AlbumTitle"}
	m := []*spanner.Mutation{
		spanner.InsertOrUpdate("Singers", singerColumns, []interface{}{1, "Marc", "Richards"}),
		spanner.InsertOrUpdate("Singers", singerColumns, []interface{}{2, "Catalina", "Smith"}),
		spanner.InsertOrUpdate("Singers", singerColumns, []interface{}{3, "Alice", "Trentor"}),
		spanner.InsertOrUpdate("Singers", singerColumns, []interface{}{4, "Lea", "Martin"}),
		spanner.InsertOrUpdate("Singers", singerColumns, []interface{}{5, "David", "Lomond"}),
		spanner.InsertOrUpdate("Albums", albumColumns, []interface{}{1, 1, "Total Junk"}),
		spanner.InsertOrUpdate("Albums", albumColumns, []interface{}{1, 2, "Go, Go, Go"}),
		spanner.InsertOrUpdate("Albums", albumColumns, []interface{}{2, 1, "Green"}),
		spanner.InsertOrUpdate("Albums", albumColumns, []interface{}{2, 2, "Forever Hold Your Peace"}),
		spanner.InsertOrUpdate("Albums", albumColumns, []interface{}{2, 3, "Terrified"}),
	}
	_, err := client.Apply(ctx, m)
	return err
}

Java

Grave os dados usando um objeto Mutation. Um objeto Mutation é um contêiner para operações de mutação. Uma Mutation representa uma sequência de inserções, atualizações e exclusões que o Cloud Spanner aplica atomicamente a diferentes linhas e tabelas em um banco de dados do Cloud Spanner.

O método newInsertBuilder() na classe Mutation cria uma mutação INSERT, que insere uma nova linha em uma tabela. Se ela já existir, a gravação falhará. Outra opção é usar o método newInsertOrUpdateBuilder para criar uma mutação INSERT_OR_UPDATE, que atualiza os valores da coluna se a linha já existir.

As mutações são gravadas pelo método write() na classe DatabaseClient. Todas as mutações em um único lote são aplicadas atomicamente.

Este código mostra como gravar os dados:

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);
}

Node.js

Grave os dados usando um objeto Table. Adicione novas linhas à tabela por meio do método Table.insert(). Todas as inserções em um único lote são aplicadas atomicamente.

Este código mostra como gravar os dados:

// Imports the Google Cloud client library
const {Spanner} = require('@google-cloud/spanner');

/**
 * TODO(developer): Uncomment the following lines before running the sample.
 */
// const projectId = 'my-project-id';
// const instanceId = 'my-instance';
// const databaseId = 'my-database';

// Creates a client
const spanner = new Spanner({
  projectId: projectId,
});

// Gets a reference to a Cloud Spanner instance and database
const instance = spanner.instance(instanceId);
const database = instance.database(databaseId);

// Instantiate Spanner table objects
const singersTable = database.table('Singers');
const albumsTable = database.table('Albums');

// Inserts rows into the Singers table
// Note: Cloud Spanner interprets Node.js numbers as FLOAT64s, so
// they must be converted to strings before being inserted as INT64s
try {
  await singersTable.insert([
    {SingerId: '1', FirstName: 'Marc', LastName: 'Richards'},
    {SingerId: '2', FirstName: 'Catalina', LastName: 'Smith'},
    {SingerId: '3', FirstName: 'Alice', LastName: 'Trentor'},
    {SingerId: '4', FirstName: 'Lea', LastName: 'Martin'},
    {SingerId: '5', FirstName: 'David', LastName: 'Lomond'},
  ]);

  await albumsTable.insert([
    {SingerId: '1', AlbumId: '1', AlbumTitle: 'Total Junk'},
    {SingerId: '1', AlbumId: '2', AlbumTitle: 'Go, Go, Go'},
    {SingerId: '2', AlbumId: '1', AlbumTitle: 'Green'},
    {SingerId: '2', AlbumId: '2', AlbumTitle: 'Forever Hold your Peace'},
    {SingerId: '2', AlbumId: '3', AlbumTitle: 'Terrified'},
  ]);

  console.log('Inserted data.');
} catch (err) {
  console.error('ERROR:', err);
} finally {
  await database.close();
}

PHP

Grave dados usando o método Database::insertBatch. insertBatch adiciona novas linhas a uma tabela. Todas as inserções em um único lote são aplicadas atomicamente.

Este código mostra como gravar os dados:

use Google\Cloud\Spanner\SpannerClient;

/**
 * Inserts sample data into the given database.
 *
 * The database and table must already exist and can be created using
 * `create_database`.
 * Example:
 * ```
 * insert_data($instanceId, $databaseId);
 * ```
 *
 * @param string $instanceId The Spanner instance ID.
 * @param string $databaseId The Spanner database ID.
 */
function insert_data($instanceId, $databaseId)
{
    $spanner = new SpannerClient();
    $instance = $spanner->instance($instanceId);
    $database = $instance->database($databaseId);

    $operation = $database->transaction(['singleUse' => true])
        ->insertBatch('Singers', [
            ['SingerId' => 1, 'FirstName' => 'Marc', 'LastName' => 'Richards'],
            ['SingerId' => 2, 'FirstName' => 'Catalina', 'LastName' => 'Smith'],
            ['SingerId' => 3, 'FirstName' => 'Alice', 'LastName' => 'Trentor'],
            ['SingerId' => 4, 'FirstName' => 'Lea', 'LastName' => 'Martin'],
            ['SingerId' => 5, 'FirstName' => 'David', 'LastName' => 'Lomond'],
        ])
        ->insertBatch('Albums', [
            ['SingerId' => 1, 'AlbumId' => 1, 'AlbumTitle' => 'Total Junk'],
            ['SingerId' => 1, 'AlbumId' => 2, 'AlbumTitle' => 'Go, Go, Go'],
            ['SingerId' => 2, 'AlbumId' => 1, 'AlbumTitle' => 'Green'],
            ['SingerId' => 2, 'AlbumId' => 2, 'AlbumTitle' => 'Forever Hold Your Peace'],
            ['SingerId' => 2, 'AlbumId' => 3, 'AlbumTitle' => 'Terrified']
        ])
        ->commit();

    print('Inserted data.' . PHP_EOL);
}

Python

Grave os dados usando um objeto Batch. Um objeto Batch é um contêiner para operações de mutação. Uma mutação representa uma sequência de inserções, atualizações, exclusões e outras ações que podem ser aplicadas atomicamente em diferentes linhas e tabelas em um banco de dados Cloud Spanner.

O método insert() na classe Batch é usado para adicionar uma ou mais mutações de inserção ao lote. Todas as mutações em um único lote são aplicadas atomicamente.

Este código mostra como gravar os dados:

def insert_data(instance_id, database_id):
    """Inserts sample data into the given database.

    The database and table must already exist and can be created using
    `create_database`.
    """
    spanner_client = spanner.Client()
    instance = spanner_client.instance(instance_id)
    database = instance.database(database_id)

    with database.batch() as batch:
        batch.insert(
            table='Singers',
            columns=('SingerId', 'FirstName', 'LastName',),
            values=[
                (1, u'Marc', u'Richards'),
                (2, u'Catalina', u'Smith'),
                (3, u'Alice', u'Trentor'),
                (4, u'Lea', u'Martin'),
                (5, u'David', u'Lomond')])

        batch.insert(
            table='Albums',
            columns=('SingerId', 'AlbumId', 'AlbumTitle',),
            values=[
                (1, 1, u'Total Junk'),
                (1, 2, u'Go, Go, Go'),
                (2, 1, u'Green'),
                (2, 2, u'Forever Hold Your Peace'),
                (2, 3, u'Terrified')])

    print('Inserted data.')

Ruby

Grave dados usando um objeto Client. O método Client#commit cria e confirma uma transação para gravações que são executadas atomicamente em um ponto lógico único no tempo em colunas, linhas e tabelas de um banco de dados.

Este código mostra como gravar os dados:

# project_id  = "Your Google Cloud project ID"
# instance_id = "Your Spanner instance ID"
# database_id = "Your Spanner database ID"

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new project: project_id
client  = spanner.client instance_id, database_id

client.commit do |c|
  c.insert "Singers", [
    { SingerId: 1, FirstName: "Marc",     LastName: "Richards" },
    { SingerId: 2, FirstName: "Catalina", LastName: "Smith"    },
    { SingerId: 3, FirstName: "Alice",    LastName: "Trentor"  },
    { SingerId: 4, FirstName: "Lea",      LastName: "Martin"   },
    { SingerId: 5, FirstName: "David",    LastName: "Lomond"   }
  ]
  c.insert "Albums", [
    { SingerId: 1, AlbumId: 1, AlbumTitle: "Total Junk"              },
    { SingerId: 1, AlbumId: 2, AlbumTitle: "Go, Go, Go"              },
    { SingerId: 2, AlbumId: 1, AlbumTitle: "Green"                   },
    { SingerId: 2, AlbumId: 2, AlbumTitle: "Forever Hold Your Peace" },
    { SingerId: 2, AlbumId: 3, AlbumTitle: "Terrified"               }
  ]
end

puts "Inserted data"

Atualizar linhas em uma tabela

Imagine que as vendas de Albums(1, 1) sejam menores do que o esperado. Como resultado, você quer transferir US$ 200.000 do orçamento de marketing de Albums(2, 2) para Albums(1, 1), mas somente se o orçamento de Albums(2, 2) for de pelo menos US$ 300.000.

Como é preciso ler os dados nas tabelas para determinar quais novos valores serão gravados, use uma transação de leitura/gravação para executar as leituras e as gravações automaticamente.

C#

Para o .NET Standard 2.0 (ou .NET 4.5) e versões mais recentes, é possível usar o TransactionScope() do .NET framework para executar uma transação. Para todas as versões compatíveis do .NET, crie uma transação definindo o resultado de SpannerConnection.BeginTransactionAsync como a propriedade Transaction de SpannerCommand.

Veja duas maneiras de executar a transação:

.NET Standard 2.0

public static async Task ReadWriteWithTransactionAsync(
    string projectId,
    string instanceId,
    string databaseId)
{
    // This sample transfers 200,000 from the MarketingBudget
    // field of the second Album to the first Album. Make sure to run
    // the addColumn and writeDataToNewColumn samples first,
    // in that order.

    string connectionString =
    $"Data Source=projects/{projectId}/instances/{instanceId}"
    + $"/databases/{databaseId}";

    using (TransactionScope scope = new TransactionScope(
        TransactionScopeAsyncFlowOption.Enabled))
    {
        decimal transferAmount = 200000;
        decimal minimumAmountToTransfer = 300000;
        decimal secondBudget = 0;
        decimal firstBudget = 0;

        // Create connection to Cloud Spanner.
        using (var connection =
            new SpannerConnection(connectionString))
        {
            // Create statement to select the second album's data.
            var cmdLookup = connection.CreateSelectCommand(
            "SELECT * FROM Albums WHERE SingerId = 2 AND AlbumId = 2");
            // Excecute the select query.
            using (var reader = await cmdLookup.ExecuteReaderAsync())
            {
                while (await reader.ReadAsync())
                {
                    // Read the second album's budget.
                    secondBudget =
                      reader.GetFieldValue<decimal>("MarketingBudget");
                    // Confirm second Album's budget is sufficient and
                    // if not raise an exception. Raising an exception
                    // will automatically roll back the transaction.
                    if (secondBudget < minimumAmountToTransfer)
                    {
                        throw new Exception("The second album's "
                            + $"budget {secondBudget} "
                            + "is less than the minimum required "
                            + "amount to transfer.");
                    }
                }
            }
            // Read the first album's budget.
            cmdLookup = connection.CreateSelectCommand(
            "SELECT * FROM Albums WHERE SingerId = 1 and AlbumId = 1");
            using (var reader = await cmdLookup.ExecuteReaderAsync())
            {
                while (await reader.ReadAsync())
                {
                    firstBudget =
                      reader.GetFieldValue<decimal>("MarketingBudget");
                }
            }

            // Specify update command parameters.
            var cmd = connection.CreateUpdateCommand("Albums",
                new SpannerParameterCollection {
                {"SingerId", SpannerDbType.Int64},
                {"AlbumId", SpannerDbType.Int64},
                {"MarketingBudget", SpannerDbType.Int64},
            });
            // Update second album to remove the transfer amount.
            secondBudget -= transferAmount;
            cmd.Parameters["SingerId"].Value = 2;
            cmd.Parameters["AlbumId"].Value = 2;
            cmd.Parameters["MarketingBudget"].Value = secondBudget;
            await cmd.ExecuteNonQueryAsync();
            // Update first album to add the transfer amount.
            firstBudget += transferAmount;
            cmd.Parameters["SingerId"].Value = 1;
            cmd.Parameters["AlbumId"].Value = 1;
            cmd.Parameters["MarketingBudget"].Value = firstBudget;
            await cmd.ExecuteNonQueryAsync();
            scope.Complete();
            Console.WriteLine("Transaction complete.");
        }
    }
}

.NET Standard 1.5

public static async Task ReadWriteWithTransactionCoreAsync(
    string projectId,
    string instanceId,
    string databaseId)
{
    // This sample transfers 200,000 from the MarketingBudget
    // field of the second Album to the first Album. Make sure to run
    // the addColumn and writeDataToNewColumn samples first,
    // in that order.
    string connectionString =
        $"Data Source=projects/{projectId}/instances/{instanceId}"
        + $"/databases/{databaseId}";

    decimal transferAmount = 200000;
    decimal minimumAmountToTransfer = 300000;
    decimal secondBudget = 0;
    decimal firstBudget = 0;

    Console.WriteLine(".NetCore API sample.");

    // Create connection to Cloud Spanner.
    using (var connection =
        new SpannerConnection(connectionString))
    {
        await connection.OpenAsync();

        // Create a readwrite transaction that we'll assign
        // to each SpannerCommand.
        using (var transaction =
                await connection.BeginTransactionAsync())
        {
            // Create statement to select the second album's data.
            var cmdLookup = connection.CreateSelectCommand(
             "SELECT * FROM Albums WHERE SingerId = 2 AND AlbumId = 2");
            cmdLookup.Transaction = transaction;
            // Excecute the select query.
            using (var reader = await cmdLookup.ExecuteReaderAsync())
            {
                while (await reader.ReadAsync())
                {
                    // Read the second album's budget.
                    secondBudget =
                       reader.GetFieldValue<decimal>("MarketingBudget");
                    // Confirm second Album's budget is sufficient and
                    // if not raise an exception. Raising an exception
                    // will automatically roll back the transaction.
                    if (secondBudget < minimumAmountToTransfer)
                    {
                        throw new Exception("The second album's "
                                + $"budget {secondBudget} "
                                + "is less than the minimum required "
                                + "amount to transfer.");
                    }
                }
            }
            // Read the first album's budget.
            cmdLookup = connection.CreateSelectCommand(
             "SELECT * FROM Albums WHERE SingerId = 1 and AlbumId = 1");
            cmdLookup.Transaction = transaction;
            using (var reader = await cmdLookup.ExecuteReaderAsync())
            {
                while (await reader.ReadAsync())
                {
                    firstBudget =
                      reader.GetFieldValue<decimal>("MarketingBudget");
                }
            }

            // Specify update command parameters.
            var cmd = connection.CreateUpdateCommand("Albums",
                new SpannerParameterCollection
                {
                    {"SingerId", SpannerDbType.Int64},
                    {"AlbumId", SpannerDbType.Int64},
                    {"MarketingBudget", SpannerDbType.Int64},
                });
            cmd.Transaction = transaction;
            // Update second album to remove the transfer amount.
            secondBudget -= transferAmount;
            cmd.Parameters["SingerId"].Value = 2;
            cmd.Parameters["AlbumId"].Value = 2;
            cmd.Parameters["MarketingBudget"].Value = secondBudget;
            await cmd.ExecuteNonQueryAsync();
            // Update first album to add the transfer amount.
            firstBudget += transferAmount;
            cmd.Parameters["SingerId"].Value = 1;
            cmd.Parameters["AlbumId"].Value = 1;
            cmd.Parameters["MarketingBudget"].Value = firstBudget;
            await cmd.ExecuteNonQueryAsync();

            await transaction.CommitAsync();
        }
        Console.WriteLine("Transaction complete.");
    }
}

Go

Use o tipo ReadWriteTransaction para executar um conjunto de trabalho no contexto de uma transação de leitura/gravação. Client.ReadWriteTransaction() retorna um objeto ReadWriteTransaction.

O exemplo usa ReadWriteTransaction.ReadRow() para recuperar uma linha de dados.

O exemplo também usa ReadWriteTransaction.BufferWrite(), que adiciona uma lista de mutações ao conjunto de atualizações aplicadas quando a transação for confirmada.

O exemplo também usa o tipo Key, que representa uma chave de linha em uma tabela ou índice do Cloud Spanner.

func writeWithTransaction(ctx context.Context, w io.Writer, client *spanner.Client) error {
	_, err := client.ReadWriteTransaction(ctx, func(ctx context.Context, txn *spanner.ReadWriteTransaction) error {
		getBudget := func(key spanner.Key) (int64, error) {
			row, err := txn.ReadRow(ctx, "Albums", key, []string{"MarketingBudget"})
			if err != nil {
				return 0, err
			}
			var budget int64
			if err := row.Column(0, &budget); err != nil {
				return 0, err
			}
			return budget, nil
		}
		album2Budget, err := getBudget(spanner.Key{2, 2})
		if err != nil {
			return err
		}
		if album2Budget >= 300000 {
			album1Budget, err := getBudget(spanner.Key{1, 1})
			if err != nil {
				return err
			}
			const transfer = 200000
			album1Budget += transfer
			album2Budget -= transfer
			cols := []string{"SingerId", "AlbumId", "MarketingBudget"}
			txn.BufferWrite([]*spanner.Mutation{
				spanner.Update("Albums", cols, []interface{}{1, 1, album1Budget}),
				spanner.Update("Albums", cols, []interface{}{2, 2, album2Budget}),
			})
		}
		return nil
	})
	return err
}

Java

Use a interface TransactionRunner para executar um conjunto de trabalho no contexto de uma transação de leitura/gravação. Ela contém o método run(), que é usado para executar uma transação de leitura/gravação com quantas tentativas forem necessárias. O método readWriteTransaction da classe DatabaseClient retorna um objeto TransactionRunner para executar uma única transação lógica.

A classe TransactionRunner.TransactionCallable contém um método run() para executar uma única tentativa de uma transação. run() pega um objeto TransactionContext, que é um contexto para uma transação.

O exemplo usa a classe Struct, que é útil para armazenar os resultados das chamadas readRow(). Ele também usa a classe Key, que representa uma chave de linha em uma tabela ou índice do Cloud Spanner.

Veja o código para executar a transação:

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;
            }
          });
}

Node.js

Use Database.runTransaction() para executar uma transação.

Veja o código para executar a transação:

// This sample transfers 200,000 from the MarketingBudget field
// of the second Album to the first Album. Make sure to run the
// addColumn and updateData samples first (in that order).

// Imports the Google Cloud client library
const {Spanner} = require('@google-cloud/spanner');

/**
 * TODO(developer): Uncomment the following lines before running the sample.
 */
// const projectId = 'my-project-id';
// const instanceId = 'my-instance';
// const databaseId = 'my-database';

// Creates a client
const spanner = new Spanner({
  projectId: projectId,
});

// Gets a reference to a Cloud Spanner instance and database
const instance = spanner.instance(instanceId);
const database = instance.database(databaseId);

const transferAmount = 200000;
const minimumAmountToTransfer = 300000;

database.runTransaction(async (err, transaction) => {
  if (err) {
    console.error(err);
    return;
  }
  let firstBudget, secondBudget;
  const queryOne = {
    columns: [`MarketingBudget`],
    keys: [[2, 2]], // SingerId: 2, AlbumId: 2
  };

  const queryTwo = {
    columns: ['MarketingBudget'],
    keys: [[1, 1]], // SingerId: 1, AlbumId: 1
  };

  Promise.all([
    // Reads the second album's budget
    transaction.read('Albums', queryOne).then(results => {
      // Gets second album's budget
      const rows = results[0].map(row => row.toJSON());
      secondBudget = rows[0].MarketingBudget;
      console.log(`The second album's marketing budget: ${secondBudget}`);

      // Makes sure the second album's budget is sufficient
      if (secondBudget < minimumAmountToTransfer) {
        throw new Error(
          `The second album's budget (${secondBudget}) is less than the minimum required amount to transfer.`
        );
      }
    }),

    // Reads the first album's budget
    transaction.read('Albums', queryTwo).then(results => {
      // Gets first album's budget
      const rows = results[0].map(row => row.toJSON());
      firstBudget = rows[0].MarketingBudget;
      console.log(`The first album's marketing budget: ${firstBudget}`);
    }),
  ])
    .then(() => {
      // Transfer the budgets between the albums
      console.log(firstBudget, secondBudget);
      firstBudget += transferAmount;
      secondBudget -= transferAmount;

      console.log(firstBudget, secondBudget);

      // Update the database
      // Note: Cloud Spanner interprets Node.js numbers as FLOAT64s, so they
      // must be converted (back) to strings before being inserted as INT64s.
      transaction.update('Albums', [
        {
          SingerId: '1',
          AlbumId: '1',
          MarketingBudget: firstBudget.toString(),
        },
        {
          SingerId: '2',
          AlbumId: '2',
          MarketingBudget: secondBudget.toString(),
        },
      ]);
    })
    .then(() => {
      // Commits the transaction and send the changes to the database
      return transaction.commit();
    })
    .then(() => {
      console.log(
        `Successfully executed read-write transaction to transfer ${transferAmount} from Album 2 to Album 1.`
      );
    })
    .catch(err => {
      console.error('ERROR:', err);
    })
    .then(() => {
      // Close the database when finished.
      return database.close();
    });
});

PHP

Use Database::runTransaction para executar uma transação.

Veja o código para executar a transação:

use Google\Cloud\Spanner\SpannerClient;
use Google\Cloud\Spanner\Transaction;
use UnexpectedValueException;

/**
 * Performs a read-write transaction to update two sample records in the
 * database.
 *
 * This will transfer 200,000 from the `MarketingBudget` field for the second
 * Album to the first Album. If the `MarketingBudget` is too low, it will
 * raise an exception.
 *
 * Before running this sample, you will need to run the `update_data` sample
 * to populate the fields.
 * Example:
 * ```
 * read_write_transaction($instanceId, $databaseId);
 * ```
 *
 * @param string $instanceId The Spanner instance ID.
 * @param string $databaseId The Spanner database ID.
 */
function read_write_transaction($instanceId, $databaseId)
{
    $spanner = new SpannerClient();
    $instance = $spanner->instance($instanceId);
    $database = $instance->database($databaseId);

    $database->runTransaction(function (Transaction $t) use ($spanner) {
        // Read the second album's budget.
        $secondAlbumKey = [2,2];
        $secondAlbumKeySet = $spanner->keySet(['keys' => [$secondAlbumKey]]);
        $secondAlbumResult = $t->read(
            'Albums',
            $secondAlbumKeySet,
            ['MarketingBudget'],
            ['limit' => 1]
        );

        $firstRow = $secondAlbumResult->rows()->current();
        $secondAlbumBudget = $firstRow['MarketingBudget'];
        if ($secondAlbumBudget < 300000) {
            // Throwing an exception will automatically roll back the transaction.
            throw new UnexpectedValueException(
                'The second album doesn\'t have enough funds to transfer'
            );
        }

        $firstAlbumKey = [1,1];
        $firstAlbumKeySet = $spanner->keySet(['keys' => [$firstAlbumKey]]);
        $firstAlbumResult = $t->read(
            'Albums',
            $firstAlbumKeySet,
            ['MarketingBudget'],
            ['limit' => 1]
        );

        // Read the first album's budget.
        $firstRow = $firstAlbumResult->rows()->current();
        $firstAlbumBudget = $firstRow['MarketingBudget'];

        // Update the budgets.
        $transferAmmount = 200000;
        $secondAlbumBudget -= $transferAmmount;
        $firstAlbumBudget += $transferAmmount;
        printf('Setting first album\'s budget to %s and the second album\'s ' .
            'budget to %s.' . PHP_EOL, $firstAlbumBudget, $secondAlbumBudget);

        // Update the rows.
        $t->updateBatch('Albums', [
            ['SingerId' => 1, 'AlbumId' => 1, 'MarketingBudget' => $firstAlbumBudget],
            ['SingerId' => 2, 'AlbumId' => 2, 'MarketingBudget' => $secondAlbumBudget],
        ]);

        // Commit the transaction!
        $t->commit();
    });

    print('Transaction complete.' . PHP_EOL);
}

Python

Use o método run_in_transaction() da classe Database para executar uma transação.

Veja o código para executar a transação:

def read_write_transaction(instance_id, database_id):
    """Performs a read-write transaction to update two sample records in the
    database.

    This will transfer 200,000 from the `MarketingBudget` field for the second
    Album to the first Album. If the `MarketingBudget` is too low, it will
    raise an exception.

    Before running this sample, you will need to run the `update_data` sample
    to populate the fields.
    """
    spanner_client = spanner.Client()
    instance = spanner_client.instance(instance_id)
    database = instance.database(database_id)

    def update_albums(transaction):
        # Read the second album budget.
        second_album_keyset = spanner.KeySet(keys=[(2, 2)])
        second_album_result = transaction.read(
            table='Albums', columns=('MarketingBudget',),
            keyset=second_album_keyset, limit=1)
        second_album_row = list(second_album_result)[0]
        second_album_budget = second_album_row[0]

        transfer_amount = 200000

        if second_album_budget < 300000:
            # Raising an exception will automatically roll back the
            # transaction.
            raise ValueError(
                'The second album doesn\'t have enough funds to transfer')

        # Read the first album's budget.
        first_album_keyset = spanner.KeySet(keys=[(1, 1)])
        first_album_result = transaction.read(
            table='Albums', columns=('MarketingBudget',),
            keyset=first_album_keyset, limit=1)
        first_album_row = list(first_album_result)[0]
        first_album_budget = first_album_row[0]

        # Update the budgets.
        second_album_budget -= transfer_amount
        first_album_budget += transfer_amount
        print(
            'Setting first album\'s budget to {} and the second album\'s '
            'budget to {}.'.format(
                first_album_budget, second_album_budget))

        # Update the rows.
        transaction.update(
            table='Albums',
            columns=(
                'SingerId', 'AlbumId', 'MarketingBudget'),
            values=[
                (1, 1, first_album_budget),
                (2, 2, second_album_budget)])

    database.run_in_transaction(update_albums)

    print('Transaction complete.')

Ruby

Use o método transaction da classe Client para executar uma transação.

Veja o código para executar a transação:

# project_id  = "Your Google Cloud project ID"
# instance_id = "Your Spanner instance ID"
# database_id = "Your Spanner database ID"

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new project: project_id
client  = spanner.client instance_id, database_id

client.transaction do |transaction|
  first_album  = transaction.read("Albums", [:MarketingBudget], keys: [[1, 1]]).rows.first
  second_album = transaction.read("Albums", [:MarketingBudget], keys: [[2, 2]]).rows.first

  raise "The second album does not have enough funds to transfer" if second_album[:MarketingBudget] < 300_000

  new_first_album_budget  = first_album[:MarketingBudget] + 200_000
  new_second_album_budget = second_album[:MarketingBudget] - 200_000

  transaction.update "Albums", [
    { SingerId: 1, AlbumId: 1, MarketingBudget: new_first_album_budget  },
    { SingerId: 2, AlbumId: 2, MarketingBudget: new_second_album_budget }
  ]
end

puts "Transaction complete"

Como excluir linhas em uma tabela

Cada biblioteca de cliente fornece várias maneiras de excluir linhas:

  • Exclua todas as linhas de uma tabela.
  • Exclua uma única linha especificando os valores da coluna-chave para a linha.
  • Exclua um grupo de linhas criando um intervalo de chaves.
  • Exclua as linhas pai para excluir as linhas em uma tabela intercalada, se ela incluir ON DELETE CASCADE na definição de esquema.

C#

Exclua linhas usando o método connection.CreateDeleteCommand(), que cria um novo SpannerCommand para fazer isso. Com o método SpannerCommand.ExecuteNonQueryAsync(), as linhas são excluídas da tabela.

Este exemplo exclui as linhas na tabela Singers individualmente. As linhas da tabela Albums são excluídas porque a tabela Albums é intercalada na tabela Singers e é definida com ON DELETE CASCADE.

public static async Task DeleteSampleDataAsync(
    string projectId, string instanceId, string databaseId)
{
    const int firstSingerId = 1;
    const int secondSingerId = 2;
    string connectionString =
        $"Data Source=projects/{projectId}/instances/{instanceId}"
        + $"/databases/{databaseId}";
    List<Singer> singers = new List<Singer> {
        new Singer {singerId = firstSingerId, firstName = "Marc",
            lastName = "Richards"},
        new Singer {singerId = secondSingerId, firstName = "Catalina",
            lastName = "Smith"},
        new Singer {singerId = 3, firstName = "Alice",
            lastName = "Trentor"},
        new Singer {singerId = 4, firstName = "Lea",
            lastName = "Martin"},
        new Singer {singerId = 5, firstName = "David",
            lastName = "Lomond"},
    };
    // Create connection to Cloud Spanner.
    using (var connection = new SpannerConnection(connectionString))
    {
        await connection.OpenAsync();

        // Delete rows from the Singers table.
        var cmd = connection.CreateDeleteCommand("Singers",
            new SpannerParameterCollection {
                {"SingerId", SpannerDbType.Int64}
            });
        await Task.WhenAll(singers.Select(singer =>
        {
            cmd.Parameters["SingerId"].Value = singer.singerId;
            return cmd.ExecuteNonQueryAsync();
        }));

        Console.WriteLine("Deleted data.");
    }
}

Go

Exclua linhas usando uma Mutation. Use o método Mutation.Delete() para criar uma mutação DELETE, que exclui uma linha. O método Client.Apply() aplica mutações atomicamente ao banco de dados.

Este exemplo exclui as linhas da tabela Albums individualmente e, em seguida, exclui todas as linhas da tabela Singers usando um KeyRange.

func delete(ctx context.Context, w io.Writer, client *spanner.Client) error {
	// Delete each of the albums by individual key,
	// then delete all the singers using a key range.
	m := []*spanner.Mutation{
		spanner.Delete("Albums", spanner.Key{1, 1}),
		spanner.Delete("Albums", spanner.Key{1, 2}),
		spanner.Delete("Albums", spanner.Key{2, 1}),
		spanner.Delete("Albums", spanner.Key{2, 2}),
		spanner.Delete("Albums", spanner.Key{2, 3}),
		spanner.Delete("Singers", spanner.KeyRange{Start: spanner.Key{1}, End: spanner.Key{5}, Kind: spanner.ClosedClosed}),
	}
	_, err := client.Apply(ctx, m)
	return err
}

Java

Exclua linhas usando o método Mutation.delete().

Este exemplo usa o método KeySet.all() para excluir todas as linhas da tabela Albums. Depois de excluir as linhas na tabela Albums, o exemplo exclui as linhas da tabela Singers individualmente usando chaves criadas com o método KeySet.singleKey().

static void deleteExampleData(DatabaseClient dbClient) {
  List<Mutation> mutations = new ArrayList<>();

  // KeySet.all() can be used to delete all the rows in a table.
  mutations.add(Mutation.delete("Albums", KeySet.all()));

  // KeySet.singleKey() can be used to delete one row at a time.
  for (Singer singer : SINGERS) {
    mutations.add(
        Mutation.delete("Singers",
            KeySet.singleKey(Key.newBuilder().append(singer.singerId).build())));
  }

  dbClient.write(mutations);
  System.out.printf("Records deleted.\n");
} 

Node.js

Exclua linhas usando o método table.deleteRows().

Este exemplo usa o método table.deleteRows() para excluir todas as linhas da tabela Singers. As linhas na tabela Albums são excluídas porque a tabela Albums é intercalada na tabela Singers e é definida com ON DELETE CASCADE.

// Imports the Google Cloud client library
const {Spanner} = require('@google-cloud/spanner');

/**
 * TODO(developer): Uncomment the following lines before running the sample.
 */
// const projectId = 'my-project-id';
// const instanceId = 'my-instance';
// const databaseId = 'my-database';

// Creates a client
const spanner = new Spanner({
  projectId: projectId,
});

// Gets a reference to a Cloud Spanner instance and database
const instance = spanner.instance(instanceId);
const database = instance.database(databaseId);

// Instantiate Spanner table object
const singersTable = database.table('Singers');

// Deletes rows from the Singers table and the Albums table,
// because Albums table is defined with ON DELETE CASCADE.
try {
  const keys = [1, 2, 3, 4, 5];
  await singersTable.deleteRows(keys);
  console.log('Deleted data.');
} catch (err) {
  console.error('ERROR:', err);
} finally {
  await database.close();
}

PHP

Exclua linhas usando o método Database::delete() method. A página do método Database::delete() inclui um exemplo.

Python

Exclua linhas usando o método Batch.delete().

Este exemplo exclui todas as linhas nas tabelas Albums e Singers individualmente usando um objeto KeySet.

def delete_data(instance_id, database_id):
    """Deletes sample data from the given database.

    The database, table, and data must already exist and can be created using
    `create_database` and `insert_data`.
    """
    spanner_client = spanner.Client()
    instance = spanner_client.instance(instance_id)
    database = instance.database(database_id)

    singers_to_delete = spanner.KeySet(
        keys=[[1], [2], [3], [4], [5]])
    albums_to_delete = spanner.KeySet(
        keys=[[1, 1], [1, 2], [2, 1], [2, 2], [2, 3]])

    with database.batch() as batch:
        batch.delete('Albums', albums_to_delete)
        batch.delete('Singers', singers_to_delete)

    print('Deleted data.')

Ruby

Exclua linhas usando o método Client#delete. A página do Client#delete tem um exemplo.

Esta página foi útil? Conte sua opinião sobre:

Enviar comentários sobre…

Documentação do Cloud Spanner