Go开源世界主流成熟ORM框架gorm实践分享
Posted itxiaoshen博客
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Go开源世界主流成熟ORM框架gorm实践分享相关的知识,希望对你有一定的参考价值。
@
概述
定义
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 中钩子方法
- 支持
Preload
、Joins
的预加载 - 事务,嵌套事务,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接口的自定义类型及其指针或别名组成,示例如:
type User struct
ID uint
Name string
Email *string
Age uint8
Birthday *time.Time
MemberNumber sql.NullString
ActivatedAt sql.NullTime
CreatedAt time.Time
UpdatedAt time.Time
GORM 倾向于约定优于配置 默认情况下,GORM 使用 ID
作为主键,使用结构体名的 蛇形复数
作为表名,字段名的 蛇形
作为列名,并使用 CreatedAt
、UpdatedAt
字段追踪创建、更新时间。如果遵循GORM采用的约定,则只需编写很少的配置/代码。如果约定不符合需求,GORM也允许指定配置。
gorm.Model
GORM定义了gorm.Model,其中包括字段ID, CreatedAt, UpdatedAt, DeletedAt,可以将其嵌入到结构中以包含这些字段
字段级权限
可导出的字段在使用 GORM 进行 CRUD 时拥有全部的权限,此外,GORM 允许您用标签控制字段级别的权限。这样就可以让一个字段的权限是只读、只写、只创建、只更新或者被忽略
type User struct
Name string `gorm:"<-:create"` // 允许读和创建
Name string `gorm:"<-:update"` // 允许读和更新
Name string `gorm:"<-"` // 允许读和写(创建和更新)
Name string `gorm:"<-:false"` // 允许读,禁止写
Name string `gorm:"->"` // 只读(除非有自定义配置,否则禁止写)
Name string `gorm:"->;<-:create"` // 允许读和写
Name string `gorm:"->:false;<-:create"` // 仅创建(禁止从 db 读)
Name string `gorm:"-"` // 通过 struct 读写会忽略该字段
Name string `gorm:"-:all"` // 通过 struct 读写、迁移会忽略该字段
Name string `gorm:"-:migration"` // 通过 struct 迁移会忽略该字段
时间惯例
GORM按惯例使用CreatedAt、UpdatedAt来跟踪创建/更新时间,如果定义了字段,GORM将在创建/更新时设置当前时间。要使用具有不同名称的字段,可以使用标签autoCreateTime、autoUpdateTime来配置这些字段。如果希望节省存储不采用时间格式改为采用UNIX(毫/纳)秒,可以简单地更改字段的数据类型。
type User struct
CreatedAt time.Time // 如果创建时为零,则设置为当前时间
UpdatedAt int // 在更新时设置为当前unix秒数,或者在创建时设置为零
Updated int64 `gorm:"autoUpdateTime:nano"` // 使用unix纳秒作为更新时间
Updated int64 `gorm:"autoUpdateTime:milli"`// 使用unix毫秒作为更新时间
Created int64 `gorm:"autoCreateTime"` // 使用unix seconds作为创建时间
嵌入结构
例如将Go内置gorm.Model结构体嵌入到User结构体里
type User struct
gorm.Model
Name string
// 这个定义等价于上面
type User struct
ID uint `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
Name string
但对于普通的struct字段,可以将其嵌入标签,例如:
type Author struct
Name string
Email string
type Blog struct
ID int
Author Author `gorm:"embedded"`
Upvotes int32
// 这个定义等价于上面Blog内嵌Author
type Blog struct
ID int64
Name string
Email string
Upvotes int32
可以使用标签embeddedPrefix为嵌入字段的数据库名称添加前缀,例如:
type Blog struct
ID int
Author Author `gorm:"embedded;embeddedPrefix:author_"`
Upvotes int32
// 这个定义等价于上面Blog内嵌Author
type Blog struct
ID int64
AuthorName string
AuthorEmail string
Upvotes int32
字段标签
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 | 在迁移时为字段添加注释 |
使用
安装
# 引入gorm
go get -u gorm.io/gorm
# 引入mySQL驱动
go get -u gorm.io/driver/mysql
# 引入sqlite驱动
go get -u gorm.io/driver/sqlite
数据库链接
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
func main()
dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config)
MySQL Driver提供了一些可以在初始化时使用的高级配置,例如:
db, err := gorm.Open(mysql.New(mysql.Config
DSN: "gorm:gorm@tcp(127.0.0.1:3306)/gorm?charset=utf8&parseTime=True&loc=Local", // data source name
DefaultStringSize: 256, // default size for string fields
DisableDatetimePrecision: true, // disable datetime precision, which not supported before MySQL 5.6
DontSupportRenameIndex: true, // drop & create when rename index, rename index not supported before MySQL 5.7, MariaDB
DontSupportRenameColumn: true, // `change` when rename column, rename column not supported before MySQL 8, MariaDB
SkipInitializeWithVersion: false, // auto configure based on currently MySQL version
), &gorm.Config)
连接池
GORM使用数据库/sql维护连接池
sqlDB, err := db.DB()// SetMaxIdleConns设置空闲连接池中的最大连接数。sqlDB.SetMaxIdleConns(10)// SetMaxOpenConns设置数据库的最大打开连接数。sqlDB.SetMaxOpenConns(100)// SetConnMaxLifetime设置连接可能被重用的最大时间。sqlDB.SetConnMaxLifetime(time.Hour)
CRUD 接口
创建
package main
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
type User struct
gorm.Model
Name string
Age int8
func main()
dsn := "root:123456@tcp(mysqlserver8:3306)/test?charset=utf8&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config)
if err != nil
fmt.Println("data error")
db.AutoMigrate(&User)
// 单个插入创建
db.Create(&UserName: "zhangsan", Age: 20)
users := []User
Name: "lisi", Age: 25,
Name: "wangwu", Age: 26,
// 多个插入并演示返回值
result := db.Create(users)
if result != nil
fmt.Println(result.RowsAffected)
fmt.Println(result.Error)
var user User
// 根据主键ID查询主键值为1的记录并返回数据
db.First(&user, 1)
fmt.Println(user)
MySQL的test数据库及对应表users信息如下:
// 指定批处理大小
var users = []UserName: "jinzhu_1", ...., Name: "jinzhu_10000"
db.CreateInBatches(users, 100)
// 初始化GORM时使用CreateBatchSize选项,所有INSERT在创建记录和关联时都将遵循此选项
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config
CreateBatchSize: 1000,
)
db := db.Session(&gorm.SessionCreateBatchSize: 1000)
users = [5000]UserName: "jinzhu", Pets: []Petpet1, pet2, pet3...
db.Create(&users)
钩子函数示例
package main
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
type User struct
gorm.Model
Name string
Age int8
func (u *User) BeforeCreate(tx *gorm.DB) (err error)
if u.Name == "admin"
fmt.Println("before create hint admin")
return
func (u *User) AfterCreate(tx *gorm.DB) (err error)
if u.Name == "admin"
fmt.Println("after create hint admin")
return
func (u *User) BeforeSave(tx *gorm.DB) (err error)
if u.Name == "admin"
fmt.Println("before save hint admin")
return
func (u *User) AfterSave(tx *gorm.DB) (err error)
if u.Name == "admin"
fmt.Println("after save hint admin")
return
func main()
dsn := "root:123456@tcp(mysqlserver8:3306)/test?charset=utf8&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config)
if err != nil
fmt.Println("data error")
db.Create(&UserName: "admin", Age: 40)
如果你想跳过Hooks方法,可以使用SkipHooks会话模式
DB.Session(&gorm.SessionSkipHooks: true).Create(&user)
DB.Session(&gorm.SessionSkipHooks: true).Create(&users)
DB.Session(&gorm.SessionSkipHooks: true).CreateInBatches(users, 100)
查询
GORM提供了First、Take、Last方法来从数据库中检索单个对象,它在查询数据库时添加了LIMIT 1条件,如果没有找到记录,它将返回错误ErrRecordNotFound。
First和Last方法将按主键顺序分别查找第一个和最后一个记录。只有当指向目标结构的指针作为参数传递给方法时,或者使用db.Model()指定模型时,它们才有效。此外,如果没有为相关模型定义主键,则模型将按第一个字段排序。
package main
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
type User struct
gorm.Model
Name string
Age int8
func (u *User) BeforeCreate(tx *gorm.DB) (err error)
if u.Name == "admin"
fmt.Println("before create hint admin")
return
func (u *User) AfterCreate(tx *gorm.DB) (err error)
if u.Name == "admin"
fmt.Println("after create hint admin")
return
func (u *User) BeforeSave(tx *gorm.DB) (err error)
if u.Name == "admin"
fmt.Println("before save hint admin")
return
func (u *User) AfterSave(tx *gorm.DB) (err error)
if u.Name == "admin"
fmt.Println("after save hint admin")
return
func main()
dsn := "root:123456@tcp(192.168.50.95:3306)/test?charset=utf8&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config)
if err != nil
fmt.Println("data error")
var user, user1, user2, user3 User
// 获取一条记录,下面相当于SELECT * FROM users ORDER BY id LIMIT 1;
db.First(&user)
fmt.Println("user=", user)
// 获取一条记录, 下面相当于SELECT * FROM users LIMIT 1;
db.Take(&user1)
fmt.Println("user1=", user1)
// 获取一条记录, 下面相当于SELECT * FROM users ORDER BY id DESC LIMIT 1;
db.Last(&user2)
fmt.Println(user2)
// 指定表获取数据放入map
result := map[string]interface
db.Table("users").Take(&result)
fmt.Println("result=", result)
db.First(&user3, "id = ?", 3)
fmt.Println("user3=", user3)
var users, users1 []User
db.Find(&users, []int1, 2, 3)
fmt.Println("users=", users)
// 查询指定字段和条件
db.Select("name").Where("id > ?", 2).Find(&users1)
fmt.Println("users1=", users1)
其他详细查看官网
// limit ,SELECT * FROM users LIMIT 3;
db.Limit(3).Find(&users)
// OFFSET ,SELECT * FROM users OFFSET 3;
db.Offset(3).Find(&users)
// group by ,SELECT name, sum(age) as total FROM `users` GROUP BY `name` HAVING name = "group"
db.Model(&User).Select("name, sum(age) as total").Group("name").Having("name = ?", "group").Find(&result)
// distinct
db.Distinct("name", "age").Order("name, age desc").Find(&results)
// join ,SELECT users.name, emails.email FROM `users` left join emails on emails.user_id = users.id
db.Model(&User).Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Scan(&result)
// scan,将结果扫描到结构体中的工作方式类似于我们使用Find的方式
var result Result
db.Table("users").Select("name", "age").Where("name = ?", "Antonio").Scan(&result)
// 原始SQL
db.Raw("SELECT name, age FROM users WHERE name = ?", "Antonio").Scan(&result)
高级查询
- 智能选择字段:GORM允许通过Select选择特定的字段,如果你经常在你的应用程序中使用这个,也许你想为API定义一个更小的结构体,它可以自动选择特定的字段。
type User struct
ID uint
Name string
Age int
Gender string
// hundreds of fields
type APIUser struct
ID uint
Name string
// 查询时自动选择“id”、“name”,SELECT `id`, `name` FROM `users` LIMIT 10
db.Model(&User).Limit(10).Find(&APIUser)
- 锁:GORM支持不同类型的锁。
// SELECT * FROM `users` FOR UPDATE
db.Clauses(clause.LockingStrength: "UPDATE").Find(&users)
// SELECT * FROM `users` FOR SHARE OF `users`
db.Clauses(clause.Locking
Strength: "SHARE",
Table: clause.TableName: clause.CurrentTable,
).Find(&users)
// SELECT * FROM `users` FOR UPDATE NOWAIT
db.Clauses(clause.Locking
Strength: "UPDATE",
Options: "NOWAIT",
).Find(&users)
- SubQuery:子查询可以嵌套在查询中,使用* GORM . db对象作为参数时,GORM可以生成子查询。
// SELECT * FROM "orders" WHERE amount > (SELECT AVG(amount) FROM "orders");
db.Where("amount > (?)", db.Table("orders").Select("AVG(amount)")).Find(&orders)
// SELECT AVG(age) as avgage FROM `users` GROUP BY `name` HAVING AVG(age) > (SELECT AVG(age) FROM `users` WHERE name LIKE "name%")
subQuery := db.Select("AVG(age)").Where("name LIKE ?", "name%").Table("users")
db.Select("AVG(age) as avgage").Group("name").Having("AVG(age) > (?)", subQuery).Find(&results)
- COUNT:获取匹配记录计数。
// SELECT count(1) FROM users WHERE name = \'jinzhu\'; (count)
db.Model(&User).Where("name = ?", "jinzhu").Count(&count)
修改
- 保存所有字段
db.First(&user)
user.Name = "jinzhu 2"
user.Age = 100
db.Save(&user)
- 更新字段
db.First(&user, "age = ?", 25)
db.Model(&user).Updates(UserName: "lisinew1", Age: 44)
db.Model(&user).Updates(map[string]interface"Name": "lisinew2", "Age": 35)
db.Model(&user).Update("age", 30)
删除
如果您的模型包含一个gorm。DeletedAt字段(包含在gorm.Model中),它将自动获得软删除能力!
// GORM允许使用带有内联条件的主键删除对象
db.Delete(&user, 1)
// 假如Email\'s ID is `10`,DELETE from emails where id = 10;
db.Delete(&email)
// 带附加条件删除
db.Where("name = ?", "jinzhu").Delete(&email)
// 永久删除
db.Unscoped().Delete(&order)
原始SQL
- 使用Scan查询Raw SQL
type Result struct
ID int
Name string
Age int
var result Result
db.Raw("SELECT id, name, age FROM users WHERE id = ?", 3).Scan(&result)
var age int
db.Raw("SELECT SUM(age) FROM users WHERE role = ?", "admin").Scan(&age)
var users []User
db.Raw("UPDATE users SET name = ? WHERE age = ? RETURNING id, name", "jinzhu", 20).Scan(&users)
- 执行原始sql
// 执行删除表数据
db.Exec("DROP TABLE users")
// 执行带有表达式的更新
db.Exec("UPDATE users SET money = ? WHERE name = ?", gorm.Expr("money * ? + ?", 10000, 1), "jinzhu")
- DryRun模式
// Session Configuration
type Session struct
DryRun bool
PrepareStmt bool
NewDB bool
Initialized bool
SkipHooks bool
SkipDefaultTransaction bool
DisableNestedTransaction bool
AllowGlobalUpdate bool
FullSaveAssociations bool
QueryFields bool
Context context.Context
Logger logger.Interface
NowFunc func() time.Time
CreateBatchSize int
// session mode
stmt := db.Session(&SessionDryRun: true).First(&user, 1).Statement
println(stmt.SQL.String())
println(stmt.Vars)
- Row & Rows
// row
row := db.Table("users").Where("name = ?", "jinzhu").Select("name", "age").Row()
row.Scan(&name, &age)
row := db.Raw("select name, age, email from users where name = ?", "jinzhu").Row()
row.Scan(&name, &age, &email)
// rows
rows, err := db.Model(&User).Where("name = ?", "jinzhu").Select("name, age, email").Rows()
defer rows.Close()
for rows.Next()
rows.Scan(&name, &age, &email)
// do something
rows, err := db.Raw("select name, age, email from users where name = ?", "jinzhu").Rows()
defer rows.Close()
for rows.Next()
rows.Scan(&name, &age, &email)
// do something
事务
GORM在事务内部执行写(创建/更新/删除)操作以确保数据一致性,如果不需要,可以在初始化时禁用它,之后将获得大约30%以上的性能提升。流程如下:
func CreateAnimals(db *gorm.DB) error
// 注意,在事务中使用tx作为数据库句柄
tx := db.Begin()
defer func()
if r := recover(); r != nil
tx.Rollback()
()
if err := tx.Error; err != nil
return err
if err := tx.Create(&AnimalName: "Giraffe").Error; err != nil
tx.Rollback()
return err
if err := tx.Create(&AnimalName: "Lion").Error; err != nil
tx.Rollback()
return err
return tx.Commit().Error
转换
- ID为主键:默认情况下,GORM使用名称为ID的字段作为表的主键。可以通过实现Tabler接口来更改默认表名,例如:
// 可以使用标记primaryKey将其他字段设置为主键
type Animal struct
ID int64
UUID string `gorm:"primaryKey"`
Name string
Age int64
- 复数表名:GORM将结构名复数化为snake_cases作为表名,对于结构User,它的表名按照约定是users
// 转换表名为my_users
func (User) TableName() string
return "my_users"
- GORM允许用户通过覆盖默认的NamingStrategy来更改默认的命名约定,该策略用于构建TableName, ColumnName, JoinTableName, RelationshipFKName, CheckerName, IndexName
type Animal struct
AnimalID int64 `gorm:"column:beast_id"` // 将列名设置为 `beast_id`
Birthday time.Time `gorm:"column:day_of_the_beast"` // 将列名设置为 `day_of_the_beast`
Age int64 `gorm:"column:age_of_the_beast"` // 将列名设置为 `age_of_the_beast`
分片
Sharding 是一个高性能的 Gorm 分表中间件。它基于 Conn 层做 SQL 拦截、AST 解析、分表路由、自增主键填充,带来的额外开销极小。对开发者友好、透明,使用上与普通 SQL、Gorm 查询无差别,只需要额外注意一下分表键条件。 为您提供高性能的数据库访问。
功能特点
- 非侵入式设计, 加载插件,指定配置,既可实现分表。
- 轻快, 非基于网络层的中间件,像 Go 一样快
- 支持多种数据库。 PostgreSQL 已通过测试,MySQL 和 SQLite 也在路上。
- 多种主键生成方式支持(Snowflake, PostgreSQL Sequence, 以及自定义支持)Snowflake 支持从主键中确定分表键。
配置分片中间件,注册想要分片的表
import (
"fmt"
"gorm.io/driver/postgres"
"gorm.io/gorm"
"gorm.io/sharding"
)
dsn := "postgres://localhost:5432/sharding-db?sslmode=disable"
db, err := gorm.Open(postgres.New(postgres.ConfigDSN: dsn))
db.Use(sharding.Register(sharding.Config
ShardingKey: "user_id",
NumberOfShards: 64,
PrimaryKeyGenerator: sharding.PKSnowflake,
, "orders").Register(sharding.Config
ShardingKey: "user_id",
NumberOfShards: 256,
PrimaryKeyGenerator: sharding.PKSnowflake,
// 对于show up give notifications, audit_logs表使用相同的分片规则。
, Notification, AuditLog))
序列化
序列化器是一个可扩展的接口,允许自定义如何使用databasae序列化和反序列化数据。
package main
import (
"database/sql/driver"
"encoding/json"
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
type Profile struct
Email string `json:"email"`
Mobile string `json:"mobile"`
type User struct
gorm.Model
Name string `json:"name"`
Age int8 `json:"age"`
Profile Profile `json:"profile" gorm:"type:json;comment:\'个人信息\'"`
// 转换表名
func (User) TableName() string
return "new_users"
// Value 存储数据的时候转换为字符串
func (t Profile) Value() (driver.Value, error)
return json.Marshal(t)
// Scan 读取数据的时候转换为json
func (t *Profile) Scan(value interface) error
return json.Unmarshal(value.([]byte), &t)
func main()
dsn := "root:123456@tcp(mysqlserver8:3306)/test?charset=utf8&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config)
if err != nil
fmt.Println("data error")
db.AutoMigrate(&User)
user := User
Name: "刘海",
Age: 23,
Profile: Profile
Email: "test1@qq.com",
Mobile: "18822334455",
,
db.Create(&user)
var user1 User
db.Debug().Where("profile->\'$.mobile\'=(?)", "18822334455").First(&user1)
fmt.Println(user1)
查看mysql已有新创建的new_users数据库和对应的数据
- 本人博客网站IT小神 www.itxiaoshen.com
从java到Go搭建Go的ORM框架Gorm
【提问】
如何使用Goland软件,搭建一个ORM框架GORM?
【解答】
具体步骤如下:
1、检查Go的安装
在任意目录执行如下命令:
go version
若有如下返回,则安装成功;如果报异常,则重新安装golang
go version go1.19.1 darwin/arm64
2、安装Gorm
在你的golang工程目录下,执行如下命令安装grom:
go get -u gorm.io/gorm
3、安装对应数据库的驱动
根据你的数据库,在任意目录执行如下命令安装驱动:
//mysql
go get -u gorm.io/driver/mysql
//sqlite
go get -u gorm.io/driver/sqlite
//sqlserver
go get -u gorm.io/driver/sqlserver
//clickhouse
go get -u gorm.io/driver/clickhouse
4、编写「数据库连接」代码
这里以连接mysql为例,在工程中创建用于数据库连接的go文件,其中dsn中内容需要根据自己数据库的信息进行替换,如下:
package db
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
var DB *gorm.DB
func InitGormDB() (err error)
dsn := "user:pwd@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config)
if err != nil
fmt.Printf("数据库连接失败:%v\\n", err)
else
fmt.Printf("数据库连接成功\\n")
DB = db
return err
同时在main.go中增加对其的调用,在服务启动时就进行数据库连接:
package main
import (
"fullstars/src/common/db"
)
func main()
db.InitGormDB()
启动服务后,可以看到连接成功的日志:
5、编写「数据Model」代码
数据库连接后,我们就对这个数据库进行一些表映射和简单查询操作。
首先,看一下我们需要操作的数据库表:
CREATE TABLE `config` (
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`type` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '类型',
`key` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`value` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`createtime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`modifytime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleteflag` tinyint(3) NOT NULL DEFAULT 0 COMMENT '逻辑删除标识',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
然后在工程中,新建Model文件:configModel.go,如下:
type Config struct
ID int
TYPE string
KEY string
VALUE string
CREATETIME time.Time
MODIFYTIME time.Time
DELETEFLAG int
func (Config) TableName() string
return "config"
需要说明的,gorm中的Model应与数据表一一对应,gorm会进行自动转换映射。
6、编写「数据查询」代码
在工程中,新建文件:configService.go,用于操作数据库和逻辑处理,如下:
其中「.Debug()」是为了在日志中打印Gorm真实拿去数据库执行的sql,方便我们查看和核对。
package config
import (
"fullstars/src/common/db"
)
func ConfigService()
var configs []Config
db.DB.Debug().Find(&configs)
var config Config
db.DB.Debug().Where("id = ?", 2).Find(&config)
同样在main.go中增加对其的调用:
package main
import (
"fullstars/src/common/db"
)
func main()
db.InitGormDB()
config.ConfigService()
调试启动后,我们可以看一下变量的结果,数据已经被查询出来了:
以上是关于Go开源世界主流成熟ORM框架gorm实践分享的主要内容,如果未能解决你的问题,请参考以下文章