如何构造一个简单的 CRUD 应用程序

why 673 2023-06-28

本文关于(如何构造一个简单的 CRUD 应用程序)。

如何构造一个简单的 CRUD 应用程序


TiDB 和 Golang 的简单 CRUD 应用程序

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

注意

推荐使用 Golang 1.16 以上版本进行 TiDB 的应用程序的编写。


第 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

GORM 预期输出


上述就是小编为大家整理的(如何构造一个简单的 CRUD 应用程序)
***

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

上一篇:MySql的复制与集群:如何实现大规模的分布式数据库
下一篇:MySQL中的批次执行优化技巧
相关文章