适用于 Go 的 Amazon QLDB 驱动程序 — 快速入门教程 - Amazon Quantum Ledger Database (Amazon QLDB)

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

适用于 Go 的 Amazon QLDB 驱动程序 — 快速入门教程

在本教程中,您将学习如何使用适用于 Go 的最新版 Amazon QLDB 驱动程序来设置简单应用程序。本指南包括安装驱动程序的步骤以及创建、读取、更新和删除 (CRUD) 的基本操作的简短代码示例。

先决条件

在开始之前,请务必执行以下操作:

  1. 请为 Go 驱动程序完成 先决条件(如果尚未执行此操作)。这包括注册 AWS、授予开发所需的编程访问权限以及安装 Go。

  2. 创建一个名为 quick-start 的分类账。

    要了解如何创建分类账,请参阅控制台入门中的 Amazon QLDB 分类账的基本操作第 1 步:创建新分类账

步骤 1:安装驱动程序

确保您的项目使用 Go 模块来安装项目依赖项。

在您的项目目录中运行以下 go get 命令。

$ go get -u github.com/awslabs/amazon-qldb-driver-go/v3/qldbdriver

安装驱动程序还会安装其依赖项,包括 AWS SDK for Go v2Amazon Ion 软件包。

第 2 步:导入软件包

导入以下 AWS 软件包。

import ( "context" "fmt" "github.com/amzn/ion-go/ion" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/qldbSession" "github.com/awslabs/amazon-qldb-driver-go/v3/qldbdriver" )

第 3 步:初始化驱动程序

初始化连接到名为 quick-start 的分类账的驱动程序实例。

cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { panic(err) } qldbSession := qldbsession.NewFromConfig(cfg, func(options *qldbsession.Options) { options.Region = "us-east-1" }) driver, err := qldbdriver.New( "quick-start", qldbSession, func(options *qldbdriver.DriverOptions) { options.LoggerVerbosity = qldbdriver.LogInfo }) if err != nil { panic(err) } defer driver.Shutdown(context.Background())
注意

在此代码示例中,将 us-east-1 替换为 AWS 区域(即您创建分类账的位置)。

第 4 步:创建表和索引

以下代码示例显示如何运行 CREATE TABLECREATE INDEX 语句。

_, err = driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) { _, err := txn.Execute("CREATE TABLE People") if err != nil { return nil, err } // When working with QLDB, it's recommended to create an index on fields we're filtering on. // This reduces the chance of OCC conflict exceptions with large datasets. _, err = txn.Execute("CREATE INDEX ON People (firstName)") if err != nil { return nil, err } _, err = txn.Execute("CREATE INDEX ON People (age)") if err != nil { return nil, err } return nil, nil }) if err != nil { panic(err) }

它会创建一个名为 People 的表,并为该表上的 firstNameage 字段创建索引。索引是优化查询性能和帮助限制乐观并发控制 (OCC) 冲突异常所必需的。

第 5 步:插入文档

以下代码示例显示如何运行 INSERT 语句。QLDB 支持 PartiQL 查询语言(兼容 SQL)和 Amazon Ion 数据格式(JSON 的超集)。

使用文字 PartiQL

以下代码使用字符串文字 PartiQL 语句将文档插入 People 表中。

_, err = driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) { return txn.Execute("INSERT INTO People {'firstName': 'Jane', 'lastName': 'Doe', 'age': 77}") }) if err != nil { panic(err) }

使用 Ion 数据类型

与 Go 内置的 JSON 包类似,您可以在 Ion 中封送和解封送 Go 数据类型。

  1. 假设您具有以下名为 Person 的 Go 结构。

    type Person struct { FirstName string `ion:"firstName"` LastName string `ion:"lastName"` Age int `ion:"age"` }
  2. 创建 Person 的实例。

    person := Person{"John", "Doe", 54}

    驱动程序会为您编组 person 的一个 ION 编码的文本表示形式。

    重要

    要使 marshal 和 unmarshal 正常工作,必须导出 Go 数据结构的字段名称(首字母大写)。

  3. person 实例传递给事务Execute的方法。

    _, err = driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) { return txn.Execute("INSERT INTO People ?", person) }) if err != nil { panic(err) }

    此示例使用问号 (?) 作为变量占位符,将文档信息传递给语句。使用占位符时,必须传递 ION 编码的文本值。

    提示

    要使用单个 INSERT 语句插入多个文档,可以向该语句传递一个列表类型的参数,如下所示。

    // people is a list txn.Execute("INSERT INTO People ?", people)

    传递 Ion 列表时,不要将变量占位符 (?) 括在双尖括号 (<<...>>) 内。在手动 PartiQL 语句中,双尖括号表示名为bag的无序集合。

第 6 步:查询文档

以下代码示例显示如何运行 SELECT 语句。

p, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) { result, err := txn.Execute("SELECT firstName, lastName, age FROM People WHERE age = 54") if err != nil { return nil, err } // Assume the result is not empty hasNext := result.Next(txn) if !hasNext && result.Err() != nil { return nil, result.Err() } ionBinary := result.GetCurrentData() temp := new(Person) err = ion.Unmarshal(ionBinary, temp) if err != nil { return nil, err } return *temp, nil }) if err != nil { panic(err) } var returnedPerson Person returnedPerson = p.(Person) if returnedPerson != person { fmt.Print("Queried result does not match inserted struct") }

此示例从 People 表中查询您的文档,假设结果集不为空,然后从结果中返回您的文档。

步骤 7:更新文档

以下代码示例显示如何运行 UPDATE 语句。

person.Age += 10 _, err = driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) { return txn.Execute("UPDATE People SET age = ? WHERE firstName = ?", person.Age, person.FirstName) }) if err != nil { panic(err) }

步骤 8:查询更新的文档

以下代码示例通过 firstName 查询 People 表,并返回结果集中的所有文档。

p, err = driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) { result, err := txn.Execute("SELECT firstName, lastName, age FROM People WHERE firstName = ?", person.FirstName) if err != nil { return nil, err } var people []Person for result.Next(txn) { ionBinary := result.GetCurrentData() temp := new(Person) err = ion.Unmarshal(ionBinary, temp) if err != nil { return nil, err } people = append(people, *temp) } if result.Err() != nil { return nil, result.Err() } return people, nil }) if err != nil { panic(err) } var people []Person people = p.([]Person) updatedPerson := Person{"John", "Doe", 64} if people[0] != updatedPerson { fmt.Print("Queried result does not match updated struct") }

第 9 步:删除表格

以下代码示例显示如何运行 DROP TABLE 语句。

_, err = driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) { return txn.Execute("DROP TABLE People") }) if err != nil { panic(err) }

运行完整的应用程序

以下代码示例是应用程序的完整版本。您还可以从头到尾复制并运行此代码示例,而不必单独执行前面的步骤。此应用程序演示了对名为 quick-start 的分类账的一些基本的 CRUD 操作。

注意

在运行此代码之前,请确保 quick-start 分类账中还没有名为 People 的活动表。

package main import ( "context" "fmt" "github.com/amzn/ion-go/ion" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/qldbsession" "github.com/awslabs/amazon-qldb-driver-go/v2/qldbdriver" ) func main() { awsSession := session.Must(session.NewSession(aws.NewConfig().WithRegion("us-east-1"))) qldbSession := qldbsession.New(awsSession) driver, err := qldbdriver.New( "quick-start", qldbSession, func(options *qldbdriver.DriverOptions) { options.LoggerVerbosity = qldbdriver.LogInfo }) if err != nil { panic(err) } defer driver.Shutdown(context.Background()) _, err = driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) { _, err := txn.Execute("CREATE TABLE People") if err != nil { return nil, err } // When working with QLDB, it's recommended to create an index on fields we're filtering on. // This reduces the chance of OCC conflict exceptions with large datasets. _, err = txn.Execute("CREATE INDEX ON People (firstName)") if err != nil { return nil, err } _, err = txn.Execute("CREATE INDEX ON People (age)") if err != nil { return nil, err } return nil, nil }) if err != nil { panic(err) } _, err = driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) { return txn.Execute("INSERT INTO People {'firstName': 'Jane', 'lastName': 'Doe', 'age': 77}") }) if err != nil { panic(err) } type Person struct { FirstName string `ion:"firstName"` LastName string `ion:"lastName"` Age int `ion:"age"` } person := Person{"John", "Doe", 54} _, err = driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) { return txn.Execute("INSERT INTO People ?", person) }) if err != nil { panic(err) } p, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) { result, err := txn.Execute("SELECT firstName, lastName, age FROM People WHERE age = 54") if err != nil { return nil, err } // Assume the result is not empty hasNext := result.Next(txn) if !hasNext && result.Err() != nil { return nil, result.Err() } ionBinary := result.GetCurrentData() temp := new(Person) err = ion.Unmarshal(ionBinary, temp) if err != nil { return nil, err } return *temp, nil }) if err != nil { panic(err) } var returnedPerson Person returnedPerson = p.(Person) if returnedPerson != person { fmt.Print("Queried result does not match inserted struct") } person.Age += 10 _, err = driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) { return txn.Execute("UPDATE People SET age = ? WHERE firstName = ?", person.Age, person.FirstName) }) if err != nil { panic(err) } p, err = driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) { result, err := txn.Execute("SELECT firstName, lastName, age FROM People WHERE firstName = ?", person.FirstName) if err != nil { return nil, err } var people []Person for result.Next(txn) { ionBinary := result.GetCurrentData() temp := new(Person) err = ion.Unmarshal(ionBinary, temp) if err != nil { return nil, err } people = append(people, *temp) } if result.Err() != nil { return nil, result.Err() } return people, nil }) if err != nil { panic(err) } var people []Person people = p.([]Person) updatedPerson := Person{"John", "Doe", 64} if people[0] != updatedPerson { fmt.Print("Queried result does not match updated struct") } _, err = driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) { return txn.Execute("DROP TABLE People") }) if err != nil { panic(err) } }