如何使用 TiDB 和 Golang 构造CRUD 应用程序?

知梧 1200 2023-06-20

本文将展示如何使用 TiDB 和 Golang 来构造一个简单的 CRUD 应用程序。

如何使用 TiDB 和 Golang 构造CRUD 应用程序?

第 1 步:启动你的 TiDB 集群

本节将介绍 TiDB 集群的启动方法。

  • TiDB Cloud

  • 本地集群

  • Gitpod

创建 TiDB Serverless 集群。

第 2 步:获取代码

git clone https://github.com/pingcap-inc/tidb-example-golang.git
  • 使用 GORM(推荐)

  • 使用 go-sql-driver/mysql

当前开源比较流行的 Golang ORM 为 GORM,此处将以 v1.23.5 版本进行说明。

封装一个用于适配 TiDB 事务的工具包 util,编写以下代码备用:

package utilimport (    "gorm.io/gorm")// TiDBGormBegin start a TiDB and Gorm transaction as a block. If no error is returned, the transaction will be committed. Otherwise, the transaction will be rolled back.func TiDBGormBegin(db *gorm.DB, pessimistic bool, fc func(tx *gorm.DB) error) (err error) {
    session := db.Session(&gorm.Session{})    if session.Error != nil {        return session.Error
    }    if pessimistic {
        session = session.Exec("set @@tidb_txn_mode=pessimistic")
    } else {
        session = session.Exec("set @@tidb_txn_mode=optimistic")
    }    if session.Error != nil {        return session.Error
    }    return session.Transaction(fc)
}

进入目录 gorm :

cd gorm

目录结构如下所示:

.
├── Makefile
├── go.mod├── go.sum└── gorm.go

其中,gorm.go 是 gorm 这个示例程序的主体。使用 gorm 时,相较于 go-sql-driver/mysql,gorm 屏蔽了创建数据库连接时,不同数据库差异的细节,其还封装了大量的操作,如 AutoMigrate、基本对象的 CRUD 等,极大的简化了代码量。

Player 是数据结构体,为数据库表在程序内的映射。Player 的每个属性都对应着 player 表的一个字段。相较于 go-sql-driver/mysql,gorm 的 Player 数据结构体为了给 gorm 提供更多的信息,加入了形如 `gorm:"primaryKey;type:VARCHAR(36);column:id"` 的注解,用来指示映射关系。

package mainimport (    "fmt"
    "math/rand"

    "github.com/google/uuid"
    "github.com/pingcap-inc/tidb-example-golang/util"

    "gorm.io/driver/mysql"
    "gorm.io/gorm"
    "gorm.io/gorm/clause"
    "gorm.io/gorm/logger")type Player struct {
    ID    string `gorm:"primaryKey;type:VARCHAR(36);column:id"`
    Coins int    `gorm:"column:coins"`
    Goods int    `gorm:"column:goods"`}func (*Player) TableName() string {    return "player"}func main() {    // 1. Configure the example database connection.
    db := createDB()    // AutoMigrate for player table
    db.AutoMigrate(&Player{})    // 2. Run some simple examples.
    simpleExample(db)    // 3. Explore more.
    tradeExample(db)
}func tradeExample(db *gorm.DB) {    // Player 1: id is "1", has only 100 coins.
    // Player 2: id is "2", has 114514 coins, and 20 goods.
    player1 := &Player{ID: "1", Coins: 100}
    player2 := &Player{ID: "2", Coins: 114514, Goods: 20}    // Create two players "by hand", using the INSERT statement on the backend.
    db.Clauses(clause.OnConflict{UpdateAll: true}).Create(player1)
    db.Clauses(clause.OnConflict{UpdateAll: true}).Create(player2)    // Player 1 wants to buy 10 goods from player 2.
    // It will cost 500 coins, but player 1 cannot afford it.
    fmt.Println("\nbuyGoods:\n    => this trade will fail")    if err := buyGoods(db, player2.ID, player1.ID, 10, 500); err == nil {        panic("there shouldn't be success")
    }    // So player 1 has to reduce the incoming quantity to two.
    fmt.Println("\nbuyGoods:\n    => this trade will success")    if err := buyGoods(db, player2.ID, player1.ID, 2, 100); err != nil {        panic(err)
    }
}func simpleExample(db *gorm.DB) {    // Create a player, who has a coin and a goods..
    if err := db.Clauses(clause.OnConflict{UpdateAll: true}).
        Create(&Player{ID: "test", Coins: 1, Goods: 1}).Error; err != nil {        panic(err)
    }    // Get a player.
    var testPlayer Player
    db.Find(&testPlayer, "id = ?", "test")
    fmt.Printf("getPlayer: %+v\n", testPlayer)    // Create players with bulk inserts. Insert 1919 players totally, with 114 players per batch.
    bulkInsertPlayers := make([]Player, 1919, 1919)
    total, batch := 1919, 114
    for i := 0; i < total; i++ {
        bulkInsertPlayers[i] = Player{
            ID:    uuid.New().String(),
            Coins: rand.Intn(10000),
            Goods: rand.Intn(10000),
        }
    }    if err := db.Session(&gorm.Session{Logger: db.Logger.LogMode(logger.Error)}).
        CreateInBatches(bulkInsertPlayers, batch).Error; err != nil {        panic(err)
    }    // Count players amount.
    playersCount := int64(0)
    db.Model(&Player{}).Count(&playersCount)
    fmt.Printf("countPlayers: %d\n", playersCount)    // Print 3 players.
    threePlayers := make([]Player, 3, 3)
    db.Limit(3).Find(&threePlayers)    for index, player := range threePlayers {
        fmt.Printf("print %d player: %+v\n", index+1, player)
    }
}func createDB() *gorm.DB {
    dsn := "root:@tcp(127.0.0.1:4000)/test?charset=utf8mb4"
    db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
        Logger: logger.Default.LogMode(logger.Info),
    })    if err != nil {        panic(err)
    }    return db
}func buyGoods(db *gorm.DB, sellID, buyID string, amount, price int) error {    return util.TiDBGormBegin(db, true, func(tx *gorm.DB) error {        var sellPlayer, buyPlayer Player        if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
            Find(&sellPlayer, "id = ?", sellID).Error; err != nil {            return err
        }        if sellPlayer.ID != sellID || sellPlayer.Goods < amount {            return fmt.Errorf("sell player %s goods not enough", sellID)
        }        if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
            Find(&buyPlayer, "id = ?", buyID).Error; err != nil {            return err
        }        if buyPlayer.ID != buyID || buyPlayer.Coins < price {            return fmt.Errorf("buy player %s coins not enough", buyID)
        }

        updateSQL := "UPDATE player set goods = goods + ?, coins = coins + ? WHERE id = ?"
        if err := tx.Exec(updateSQL, -amount, price, sellID).Error; err != nil {            return err
        }        if err := tx.Exec(updateSQL, amount, -price, buyID).Error; err != nil {            return err
        }

        fmt.Println("\n[buyGoods]:\n    'trade success'")        return nil
    })
}第 3 步:运行代码

本节将逐步介绍代码的运行方法。

第 3 步第 1 部分:go-sql-driver/mysql 表初始化

  • 使用 GORM(推荐)

  • 使用 go-sql-driver/mysql

注意

在 Gitpod Playground 中尝试 GORM: 现在就试试

无需手动初始化表。

第 3 步第 2 部分:TiDB Cloud 更改参数

  • 使用 GORM(推荐)

  • 使用 go-sql-driver/mysql

若你使用 TiDB Serverless 集群,更改 gorm.go 内 dsn 参数值:

dsn := "root:@tcp(127.0.0.1:4000)/test?charset=utf8mb4"

若你设定的密码为 123456,而且从 TiDB Serverless 集群面板中得到的连接信息为:

  • Endpoint: xxx.tidbcloud.com

  • Port: 4000

  • User: 2aEp24QWEDLqRFs.root

那么此处应将 mysql.RegisterTLSConfig 和 dsn 更改为:

mysql.RegisterTLSConfig("register-tidb-tls", &tls.Config {
    MinVersion: tls.VersionTLS12,
    ServerName: "xxx.tidbcloud.com",
})

dsn := "2aEp24QWEDLqRFs.root:123456@tcp(xxx.tidbcloud.com:4000)/test?charset=utf8mb4&tls=register-tidb-tls"
第 3 步第 3 部分:运行
  • 使用 GORM(推荐)

  • 使用 go-sql-driver/mysql

运行 make all,这是以下两个操作的组合:

  • 构建二进制 (make build):go build -o bin/gorm-example

  • 运行 (make run):./bin/gorm-example

你也可以单独运行这两个 make 命令或原生命令。

第 4 步:预期输出

  • 使用 GORM(推荐)

  • 使用 go-sql-driver/mysql

以上就是小编为大家整理的关于如何使用 TiDB 和 Golang 构造CRUD 应用程序的相关内容。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:TiDB 的增删改查 SQL 的使用方法,基本 SQL 操作介绍
下一篇:使用 TiDB 构建 Spring Boot Web 应用程序的详细步骤,一文说清楚
相关文章