经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » Go语言 » 查看文章
Go开源世界主流成熟ORM框架gorm实践分享
来源:cnblogs  作者:itxiaoshen  时间:2023/5/17 8:52:32  对本文有异议

@

概述

定义

GORM 官网地址 https://gorm.io/ 最新版本v1.25.1

GORM 官网文档地址 https://gorm.io/docs/

GORM 源码地址 https://github.com/go-gorm/gorm

GORM 是Golang语言中一个功能齐全的优秀的ORM 框架,对开发者友好,支持多种数据库,并提供了丰富的功能和 API,可以让开发者更加方便地进行数据库操作。

核心功能

  • ORM功能丰富、完整
  • 关联 (Has One,Has Many,Belongs To,Many To Many,多态,单表继承)
  • Create,Save,Update,Delete,Find 中钩子方法
  • 支持 PreloadJoins 的预加载
  • 事务,嵌套事务,Save Point,Rollback To Saved Point
  • Context、预编译模式、DryRun 模式
  • 批量插入,FindInBatches,Find/Create with Map,使用 SQL 表达式、Context Valuer 进行 CRUD
  • SQL 构建器,Upsert,数据库锁,Optimizer/Index/Comment Hint,命名参数,子查询
  • 复合主键,索引,约束
  • Auto Migration
  • 自定义 Logger
  • 灵活的可扩展插件 API:Database Resolver(多数据库,读写分离)、Prometheus…
  • 每个特性都经过了测试的重重考验
  • 开发者友好

声明模型与约定

模型是标准的 struct,由 Go 的基本数据类型、实现了 Scanner和 Valuer接口的自定义类型及其指针或别名组成,示例如:

  1. type User struct {
  2. ID uint
  3. Name string
  4. Email *string
  5. Age uint8
  6. Birthday *time.Time
  7. MemberNumber sql.NullString
  8. ActivatedAt sql.NullTime
  9. CreatedAt time.Time
  10. UpdatedAt time.Time
  11. }

GORM 倾向于约定优于配置 默认情况下,GORM 使用 ID 作为主键,使用结构体名的 蛇形复数 作为表名,字段名的 蛇形 作为列名,并使用 CreatedAtUpdatedAt 字段追踪创建、更新时间。如果遵循GORM采用的约定,则只需编写很少的配置/代码。如果约定不符合需求,GORM也允许指定配置。

gorm.Model

GORM定义了gorm.Model,其中包括字段ID, CreatedAt, UpdatedAt, DeletedAt,可以将其嵌入到结构中以包含这些字段

image-20230515164255737

字段级权限

可导出的字段在使用 GORM 进行 CRUD 时拥有全部的权限,此外,GORM 允许您用标签控制字段级别的权限。这样就可以让一个字段的权限是只读、只写、只创建、只更新或者被忽略

  1. type User struct {
  2. Name string `gorm:"<-:create"` // 允许读和创建
  3. Name string `gorm:"<-:update"` // 允许读和更新
  4. Name string `gorm:"<-"` // 允许读和写(创建和更新)
  5. Name string `gorm:"<-:false"` // 允许读,禁止写
  6. Name string `gorm:"->"` // 只读(除非有自定义配置,否则禁止写)
  7. Name string `gorm:"->;<-:create"` // 允许读和写
  8. Name string `gorm:"->:false;<-:create"` // 仅创建(禁止从 db 读)
  9. Name string `gorm:"-"` // 通过 struct 读写会忽略该字段
  10. Name string `gorm:"-:all"` // 通过 struct 读写、迁移会忽略该字段
  11. Name string `gorm:"-:migration"` // 通过 struct 迁移会忽略该字段
  12. }

时间惯例

GORM按惯例使用CreatedAt、UpdatedAt来跟踪创建/更新时间,如果定义了字段,GORM将在创建/更新时设置当前时间。要使用具有不同名称的字段,可以使用标签autoCreateTime、autoUpdateTime来配置这些字段。如果希望节省存储不采用时间格式改为采用UNIX(毫/纳)秒,可以简单地更改字段的数据类型。

  1. type User struct {
  2. CreatedAt time.Time // 如果创建时为零,则设置为当前时间
  3. UpdatedAt int // 在更新时设置为当前unix秒数,或者在创建时设置为零
  4. Updated int64 `gorm:"autoUpdateTime:nano"` // 使用unix纳秒作为更新时间
  5. Updated int64 `gorm:"autoUpdateTime:milli"`// 使用unix毫秒作为更新时间
  6. Created int64 `gorm:"autoCreateTime"` // 使用unix seconds作为创建时间
  7. }

嵌入结构

例如将Go内置gorm.Model结构体嵌入到User结构体里

  1. type User struct {
  2. gorm.Model
  3. Name string
  4. }
  5. // 这个定义等价于上面
  6. type User struct {
  7. ID uint `gorm:"primaryKey"`
  8. CreatedAt time.Time
  9. UpdatedAt time.Time
  10. DeletedAt gorm.DeletedAt `gorm:"index"`
  11. Name string
  12. }

但对于普通的struct字段,可以将其嵌入标签,例如:

  1. type Author struct {
  2. Name string
  3. Email string
  4. }
  5. type Blog struct {
  6. ID int
  7. Author Author `gorm:"embedded"`
  8. Upvotes int32
  9. }
  10. // 这个定义等价于上面Blog内嵌Author
  11. type Blog struct {
  12. ID int64
  13. Name string
  14. Email string
  15. Upvotes int32
  16. }

可以使用标签embeddedPrefix为嵌入字段的数据库名称添加前缀,例如:

  1. type Blog struct {
  2. ID int
  3. Author Author `gorm:"embedded;embeddedPrefix:author_"`
  4. Upvotes int32
  5. }
  6. // 这个定义等价于上面Blog内嵌Author
  7. type Blog struct {
  8. ID int64
  9. AuthorName string
  10. AuthorEmail string
  11. Upvotes int32
  12. }

字段标签

Tag Name Description
column 数据库表列名
type 列数据类型,例如:bool, int, uint, float, string, time, bytes,这适用于所有数据库,并且可以与其他标签一起使用,如' not null ', ' size ', ' autoIncrement '…指定的数据库数据类型,如' varbinary(8) '也支持,当使用指定的数据库数据类型时,它需要是一个完整的数据库数据类型,例如: MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT
serializer 指定如何将数据序列化和反序列化到db的序列化器, e.g: serializer:json/gob/unixtime
size 指定列数据大小/长度, e.g: size:256
primaryKey 指定列为主键
unique 将列指定为唯一约束
default 指定列默认值
precision 指定列精度
scale 指定列的比例
not null 指定列为NOT NULL
autoIncrement 指定列可自动递增
autoIncrementIncrement 自动递增步长,控制连续列值之间的间隔
embedded 嵌入字段
embeddedPrefix 嵌入字段的列名前缀
autoCreateTime 创建时跟踪当前时间,对于' int '字段,它将跟踪Unix秒,使用值' nano ' / ' milli '来跟踪Unix纳米/毫秒, e.g: autoCreateTime:nano
autoUpdateTime 在创建/更新时跟踪当前时间,对于' int '字段,它将跟踪Unix秒,使用值' nano ' / ' milli '来跟踪Unix纳米/毫秒, e.g: autoUpdateTime:milli
index 对多个字段使用相同的名称创建复合索引
uniqueIndex 与' index '相同,但创建唯一的索引
check 创建检查约束, eg: check:age > 13
<- 设置字段的写权限,' <-:create ' create-only字段,' <-:update ' update-only字段,' <-:false '无写权限,' <- '创建和更新权限
-> 设置字段的读权限,' ->:false '没有读权限
- 忽略该字段,' - '无读写权限,' -:migration '无迁移权限,' -:all '无读写迁移权限
comment 在迁移时为字段添加注释

使用

安装

  1. # 引入gorm
  2. go get -u gorm.io/gorm
  3. # 引入mySQL驱动
  4. go get -u gorm.io/driver/mysql
  5. # 引入sqlite驱动
  6. go get -u gorm.io/driver/sqlite

数据库链接

  1. import (
  2. "gorm.io/driver/mysql"
  3. "gorm.io/gorm"
  4. )
  5. func main() {
  6. dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
  7. db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
  8. }

MySQL Driver提供了一些可以在初始化时使用的高级配置,例如:

  1. db, err := gorm.Open(mysql.New(mysql.Config{
  2. DSN: "gorm:gorm@tcp(127.0.0.1:3306)/gorm?charset=utf8&parseTime=True&loc=Local", // data source name
  3. DefaultStringSize: 256, // default size for string fields
  4. DisableDatetimePrecision: true, // disable datetime precision, which not supported before MySQL 5.6
  5. DontSupportRenameIndex: true, // drop & create when rename index, rename index not supported before MySQL 5.7, MariaDB
  6. DontSupportRenameColumn: true, // `change` when rename column, rename column not supported before MySQL 8, MariaDB
  7. SkipInitializeWithVersion: false, // auto configure based on currently MySQL version
  8. }), &gorm.Config{})

连接池

GORM使用数据库/sql维护连接池

  1. sqlDB, err := db.DB()// SetMaxIdleConns设置空闲连接池中的最大连接数。sqlDB.SetMaxIdleConns(10)// SetMaxOpenConns设置数据库的最大打开连接数。sqlDB.SetMaxOpenConns(100)// SetConnMaxLifetime设置连接可能被重用的最大时间。sqlDB.SetConnMaxLifetime(time.Hour)

CRUD 接口

创建

  1. package main
  2. import (
  3. "fmt"
  4. "gorm.io/driver/mysql"
  5. "gorm.io/gorm"
  6. )
  7. type User struct {
  8. gorm.Model
  9. Name string
  10. Age int8
  11. }
  12. func main() {
  13. dsn := "root:123456@tcp(mysqlserver8:3306)/test?charset=utf8&parseTime=True&loc=Local"
  14. db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
  15. if err != nil {
  16. fmt.Println("data error")
  17. }
  18. db.AutoMigrate(&User{})
  19. // 单个插入创建
  20. db.Create(&User{Name: "zhangsan", Age: 20})
  21. users := []User{
  22. {Name: "lisi", Age: 25},
  23. {Name: "wangwu", Age: 26},
  24. }
  25. // 多个插入并演示返回值
  26. result := db.Create(users)
  27. if result != nil {
  28. fmt.Println(result.RowsAffected)
  29. fmt.Println(result.Error)
  30. }
  31. var user User
  32. // 根据主键ID查询主键值为1的记录并返回数据
  33. db.First(&user, 1)
  34. fmt.Println(user)
  35. }

image-20230516101850485

MySQL的test数据库及对应表users信息如下:

image-20230516101802080

  1. // 指定批处理大小
  2. var users = []User{{Name: "jinzhu_1"}, ...., {Name: "jinzhu_10000"}}
  3. db.CreateInBatches(users, 100)
  4. // 初始化GORM时使用CreateBatchSize选项,所有INSERT在创建记录和关联时都将遵循此选项
  5. db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
  6. CreateBatchSize: 1000,
  7. })
  8. db := db.Session(&gorm.Session{CreateBatchSize: 1000})
  9. users = [5000]User{{Name: "jinzhu", Pets: []Pet{pet1, pet2, pet3}}...}
  10. db.Create(&users)

钩子函数示例

  1. package main
  2. import (
  3. "fmt"
  4. "gorm.io/driver/mysql"
  5. "gorm.io/gorm"
  6. )
  7. type User struct {
  8. gorm.Model
  9. Name string
  10. Age int8
  11. }
  12. func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
  13. if u.Name == "admin" {
  14. fmt.Println("before create hint admin")
  15. }
  16. return
  17. }
  18. func (u *User) AfterCreate(tx *gorm.DB) (err error) {
  19. if u.Name == "admin" {
  20. fmt.Println("after create hint admin")
  21. }
  22. return
  23. }
  24. func (u *User) BeforeSave(tx *gorm.DB) (err error) {
  25. if u.Name == "admin" {
  26. fmt.Println("before save hint admin")
  27. }
  28. return
  29. }
  30. func (u *User) AfterSave(tx *gorm.DB) (err error) {
  31. if u.Name == "admin" {
  32. fmt.Println("after save hint admin")
  33. }
  34. return
  35. }
  36. func main() {
  37. dsn := "root:123456@tcp(mysqlserver8:3306)/test?charset=utf8&parseTime=True&loc=Local"
  38. db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
  39. if err != nil {
  40. fmt.Println("data error")
  41. }
  42. db.Create(&User{Name: "admin", Age: 40})
  43. }

image-20230516104842547

如果你想跳过Hooks方法,可以使用SkipHooks会话模式

  1. DB.Session(&gorm.Session{SkipHooks: true}).Create(&user)
  2. DB.Session(&gorm.Session{SkipHooks: true}).Create(&users)
  3. DB.Session(&gorm.Session{SkipHooks: true}).CreateInBatches(users, 100)

查询

GORM提供了First、Take、Last方法来从数据库中检索单个对象,它在查询数据库时添加了LIMIT 1条件,如果没有找到记录,它将返回错误ErrRecordNotFound。

First和Last方法将按主键顺序分别查找第一个和最后一个记录。只有当指向目标结构的指针作为参数传递给方法时,或者使用db.Model()指定模型时,它们才有效。此外,如果没有为相关模型定义主键,则模型将按第一个字段排序。

  1. package main
  2. import (
  3. "fmt"
  4. "gorm.io/driver/mysql"
  5. "gorm.io/gorm"
  6. )
  7. type User struct {
  8. gorm.Model
  9. Name string
  10. Age int8
  11. }
  12. func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
  13. if u.Name == "admin" {
  14. fmt.Println("before create hint admin")
  15. }
  16. return
  17. }
  18. func (u *User) AfterCreate(tx *gorm.DB) (err error) {
  19. if u.Name == "admin" {
  20. fmt.Println("after create hint admin")
  21. }
  22. return
  23. }
  24. func (u *User) BeforeSave(tx *gorm.DB) (err error) {
  25. if u.Name == "admin" {
  26. fmt.Println("before save hint admin")
  27. }
  28. return
  29. }
  30. func (u *User) AfterSave(tx *gorm.DB) (err error) {
  31. if u.Name == "admin" {
  32. fmt.Println("after save hint admin")
  33. }
  34. return
  35. }
  36. func main() {
  37. dsn := "root:123456@tcp(192.168.50.95:3306)/test?charset=utf8&parseTime=True&loc=Local"
  38. db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
  39. if err != nil {
  40. fmt.Println("data error")
  41. }
  42. var user, user1, user2, user3 User
  43. // 获取一条记录,下面相当于SELECT * FROM users ORDER BY id LIMIT 1;
  44. db.First(&user)
  45. fmt.Println("user=", user)
  46. // 获取一条记录, 下面相当于SELECT * FROM users LIMIT 1;
  47. db.Take(&user1)
  48. fmt.Println("user1=", user1)
  49. // 获取一条记录, 下面相当于SELECT * FROM users ORDER BY id DESC LIMIT 1;
  50. db.Last(&user2)
  51. fmt.Println(user2)
  52. // 指定表获取数据放入map
  53. result := map[string]interface{}{}
  54. db.Table("users").Take(&result)
  55. fmt.Println("result=", result)
  56. db.First(&user3, "id = ?", 3)
  57. fmt.Println("user3=", user3)
  58. var users, users1 []User
  59. db.Find(&users, []int{1, 2, 3})
  60. fmt.Println("users=", users)
  61. // 查询指定字段和条件
  62. db.Select("name").Where("id > ?", 2).Find(&users1)
  63. fmt.Println("users1=", users1)
  64. }

image-20230516155547556

其他详细查看官网

  1. // limit ,SELECT * FROM users LIMIT 3;
  2. db.Limit(3).Find(&users)
  3. // OFFSET ,SELECT * FROM users OFFSET 3;
  4. db.Offset(3).Find(&users)
  5. // group by ,SELECT name, sum(age) as total FROM `users` GROUP BY `name` HAVING name = "group"
  6. db.Model(&User{}).Select("name, sum(age) as total").Group("name").Having("name = ?", "group").Find(&result)
  7. // distinct
  8. db.Distinct("name", "age").Order("name, age desc").Find(&results)
  9. // join ,SELECT users.name, emails.email FROM `users` left join emails on emails.user_id = users.id
  10. db.Model(&User{}).Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Scan(&result{})
  11. // scan,将结果扫描到结构体中的工作方式类似于我们使用Find的方式
  12. var result Result
  13. db.Table("users").Select("name", "age").Where("name = ?", "Antonio").Scan(&result)
  14. // 原始SQL
  15. db.Raw("SELECT name, age FROM users WHERE name = ?", "Antonio").Scan(&result)

高级查询

  • 智能选择字段:GORM允许通过Select选择特定的字段,如果你经常在你的应用程序中使用这个,也许你想为API定义一个更小的结构体,它可以自动选择特定的字段。
  1. type User struct {
  2. ID uint
  3. Name string
  4. Age int
  5. Gender string
  6. // hundreds of fields
  7. }
  8. type APIUser struct {
  9. ID uint
  10. Name string
  11. }
  12. // 查询时自动选择“id”、“name”,SELECT `id`, `name` FROM `users` LIMIT 10
  13. db.Model(&User{}).Limit(10).Find(&APIUser{})
  • 锁:GORM支持不同类型的锁。
  1. // SELECT * FROM `users` FOR UPDATE
  2. db.Clauses(clause.Locking{Strength: "UPDATE"}).Find(&users)
  3. // SELECT * FROM `users` FOR SHARE OF `users`
  4. db.Clauses(clause.Locking{
  5. Strength: "SHARE",
  6. Table: clause.Table{Name: clause.CurrentTable},
  7. }).Find(&users)
  8. // SELECT * FROM `users` FOR UPDATE NOWAIT
  9. db.Clauses(clause.Locking{
  10. Strength: "UPDATE",
  11. Options: "NOWAIT",
  12. }).Find(&users)
  • SubQuery:子查询可以嵌套在查询中,使用* GORM . db对象作为参数时,GORM可以生成子查询。
  1. // SELECT * FROM "orders" WHERE amount > (SELECT AVG(amount) FROM "orders");
  2. db.Where("amount > (?)", db.Table("orders").Select("AVG(amount)")).Find(&orders)
  3. // SELECT AVG(age) as avgage FROM `users` GROUP BY `name` HAVING AVG(age) > (SELECT AVG(age) FROM `users` WHERE name LIKE "name%")
  4. subQuery := db.Select("AVG(age)").Where("name LIKE ?", "name%").Table("users")
  5. db.Select("AVG(age) as avgage").Group("name").Having("AVG(age) > (?)", subQuery).Find(&results)
  • COUNT:获取匹配记录计数。
  1. // SELECT count(1) FROM users WHERE name = 'jinzhu'; (count)
  2. db.Model(&User{}).Where("name = ?", "jinzhu").Count(&count)

修改

  • 保存所有字段
  1. db.First(&user)
  2. user.Name = "jinzhu 2"
  3. user.Age = 100
  4. db.Save(&user)
  • 更新字段
  1. db.First(&user, "age = ?", 25)
  2. db.Model(&user).Updates(User{Name: "lisinew1", Age: 44})
  3. db.Model(&user).Updates(map[string]interface{}{"Name": "lisinew2", "Age": 35})
  4. db.Model(&user).Update("age", 30)

删除

如果您的模型包含一个gorm。DeletedAt字段(包含在gorm.Model中),它将自动获得软删除能力!

  1. // GORM允许使用带有内联条件的主键删除对象
  2. db.Delete(&user, 1)
  3. // 假如Email's ID is `10`,DELETE from emails where id = 10;
  4. db.Delete(&email)
  5. // 带附加条件删除
  6. db.Where("name = ?", "jinzhu").Delete(&email)
  7. // 永久删除
  8. db.Unscoped().Delete(&order)

原始SQL

  • 使用Scan查询Raw SQL
  1. type Result struct {
  2. ID int
  3. Name string
  4. Age int
  5. }
  6. var result Result
  7. db.Raw("SELECT id, name, age FROM users WHERE id = ?", 3).Scan(&result)
  8. var age int
  9. db.Raw("SELECT SUM(age) FROM users WHERE role = ?", "admin").Scan(&age)
  10. var users []User
  11. db.Raw("UPDATE users SET name = ? WHERE age = ? RETURNING id, name", "jinzhu", 20).Scan(&users)
  • 执行原始sql
  1. // 执行删除表数据
  2. db.Exec("DROP TABLE users")
  3. // 执行带有表达式的更新
  4. db.Exec("UPDATE users SET money = ? WHERE name = ?", gorm.Expr("money * ? + ?", 10000, 1), "jinzhu")
  • DryRun模式
  1. // Session Configuration
  2. type Session struct {
  3. DryRun bool
  4. PrepareStmt bool
  5. NewDB bool
  6. Initialized bool
  7. SkipHooks bool
  8. SkipDefaultTransaction bool
  9. DisableNestedTransaction bool
  10. AllowGlobalUpdate bool
  11. FullSaveAssociations bool
  12. QueryFields bool
  13. Context context.Context
  14. Logger logger.Interface
  15. NowFunc func() time.Time
  16. CreateBatchSize int
  17. }
  18. // session mode
  19. stmt := db.Session(&Session{DryRun: true}).First(&user, 1).Statement
  20. println(stmt.SQL.String())
  21. println(stmt.Vars)

image-20230516164620140

  • Row & Rows
  1. // row
  2. row := db.Table("users").Where("name = ?", "jinzhu").Select("name", "age").Row()
  3. row.Scan(&name, &age)
  4. row := db.Raw("select name, age, email from users where name = ?", "jinzhu").Row()
  5. row.Scan(&name, &age, &email)
  6. // rows
  7. rows, err := db.Model(&User{}).Where("name = ?", "jinzhu").Select("name, age, email").Rows()
  8. defer rows.Close()
  9. for rows.Next() {
  10. rows.Scan(&name, &age, &email)
  11. // do something
  12. }
  13. rows, err := db.Raw("select name, age, email from users where name = ?", "jinzhu").Rows()
  14. defer rows.Close()
  15. for rows.Next() {
  16. rows.Scan(&name, &age, &email)
  17. // do something
  18. }

事务

GORM在事务内部执行写(创建/更新/删除)操作以确保数据一致性,如果不需要,可以在初始化时禁用它,之后将获得大约30%以上的性能提升。流程如下:

  1. func CreateAnimals(db *gorm.DB) error {
  2. // 注意,在事务中使用tx作为数据库句柄
  3. tx := db.Begin()
  4. defer func() {
  5. if r := recover(); r != nil {
  6. tx.Rollback()
  7. }
  8. }()
  9. if err := tx.Error; err != nil {
  10. return err
  11. }
  12. if err := tx.Create(&Animal{Name: "Giraffe"}).Error; err != nil {
  13. tx.Rollback()
  14. return err
  15. }
  16. if err := tx.Create(&Animal{Name: "Lion"}).Error; err != nil {
  17. tx.Rollback()
  18. return err
  19. }
  20. return tx.Commit().Error
  21. }

转换

  • ID为主键:默认情况下,GORM使用名称为ID的字段作为表的主键。可以通过实现Tabler接口来更改默认表名,例如:
  1. // 可以使用标记primaryKey将其他字段设置为主键
  2. type Animal struct {
  3. ID int64
  4. UUID string `gorm:"primaryKey"`
  5. Name string
  6. Age int64
  7. }
  • 复数表名:GORM将结构名复数化为snake_cases作为表名,对于结构User,它的表名按照约定是users
  1. // 转换表名为my_users
  2. func (User) TableName() string {
  3. return "my_users"
  4. }
  • GORM允许用户通过覆盖默认的NamingStrategy来更改默认的命名约定,该策略用于构建TableName, ColumnName, JoinTableName, RelationshipFKName, CheckerName, IndexName
  1. type Animal struct {
  2. AnimalID int64 `gorm:"column:beast_id"` // 将列名设置为 `beast_id`
  3. Birthday time.Time `gorm:"column:day_of_the_beast"` // 将列名设置为 `day_of_the_beast`
  4. Age int64 `gorm:"column:age_of_the_beast"` // 将列名设置为 `age_of_the_beast`
  5. }

分片

Sharding 是一个高性能的 Gorm 分表中间件。它基于 Conn 层做 SQL 拦截、AST 解析、分表路由、自增主键填充,带来的额外开销极小。对开发者友好、透明,使用上与普通 SQL、Gorm 查询无差别,只需要额外注意一下分表键条件。 为您提供高性能的数据库访问。

功能特点

  • 非侵入式设计, 加载插件,指定配置,既可实现分表。
  • 轻快, 非基于网络层的中间件,像 Go 一样快
  • 支持多种数据库。 PostgreSQL 已通过测试,MySQL 和 SQLite 也在路上。
  • 多种主键生成方式支持(Snowflake, PostgreSQL Sequence, 以及自定义支持)Snowflake 支持从主键中确定分表键。

配置分片中间件,注册想要分片的表

  1. import (
  2. "fmt"
  3. "gorm.io/driver/postgres"
  4. "gorm.io/gorm"
  5. "gorm.io/sharding"
  6. )
  7. dsn := "postgres://localhost:5432/sharding-db?sslmode=disable"
  8. db, err := gorm.Open(postgres.New(postgres.Config{DSN: dsn}))
  9. db.Use(sharding.Register(sharding.Config{
  10. ShardingKey: "user_id",
  11. NumberOfShards: 64,
  12. PrimaryKeyGenerator: sharding.PKSnowflake,
  13. }, "orders").Register(sharding.Config{
  14. ShardingKey: "user_id",
  15. NumberOfShards: 256,
  16. PrimaryKeyGenerator: sharding.PKSnowflake,
  17. // 对于show up give notifications, audit_logs表使用相同的分片规则。
  18. }, Notification{}, AuditLog{}))

序列化

序列化器是一个可扩展的接口,允许自定义如何使用databasae序列化和反序列化数据。

  1. package main
  2. import (
  3. "database/sql/driver"
  4. "encoding/json"
  5. "fmt"
  6. "gorm.io/driver/mysql"
  7. "gorm.io/gorm"
  8. )
  9. type Profile struct {
  10. Email string `json:"email"`
  11. Mobile string `json:"mobile"`
  12. }
  13. type User struct {
  14. gorm.Model
  15. Name string `json:"name"`
  16. Age int8 `json:"age"`
  17. Profile Profile `json:"profile" gorm:"type:json;comment:'个人信息'"`
  18. }
  19. // 转换表名
  20. func (User) TableName() string {
  21. return "new_users"
  22. }
  23. // Value 存储数据的时候转换为字符串
  24. func (t Profile) Value() (driver.Value, error) {
  25. return json.Marshal(t)
  26. }
  27. // Scan 读取数据的时候转换为json
  28. func (t *Profile) Scan(value interface{}) error {
  29. return json.Unmarshal(value.([]byte), &t)
  30. }
  31. func main() {
  32. dsn := "root:123456@tcp(mysqlserver8:3306)/test?charset=utf8&parseTime=True&loc=Local"
  33. db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
  34. if err != nil {
  35. fmt.Println("data error")
  36. }
  37. db.AutoMigrate(&User{})
  38. user := User{
  39. Name: "刘海",
  40. Age: 23,
  41. Profile: Profile{
  42. Email: "test1@qq.com",
  43. Mobile: "18822334455",
  44. },
  45. }
  46. db.Create(&user)
  47. var user1 User
  48. db.Debug().Where("profile->'$.mobile'=(?)", "18822334455").First(&user1)
  49. fmt.Println(user1)
  50. }

image-20230516174834983

查看mysql已有新创建的new_users数据库和对应的数据

image-20230516175004615

  • 本人博客网站IT小神 www.itxiaoshen.com

原文链接:https://www.cnblogs.com/itxiaoshen/p/17407238.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号