GORM - The fantastic ORM library for Golang, aims to be developer friendly.GORM - The fantastic ORM library for Golang, aims to be developer friendly.
GORM 通过将 Go 结构体(Go structs) 映射到数据库表来简化数据库交互。
1.定义
对象关系映射(ORM) 是一种技术,用于解决面向对象编程与关系数据库之间的互不匹配问题。ORM通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库。有了ORM,在大部分场景中,可以像操作对象一般操作数据库,而不需要使用复杂的SQL语句。
2.下载
go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql
3.连接数据库
gorm.Open(mysql.Open(dsn), &gorm.Config{
func Open(dialector Dialector, opts ...Option) (db *DB, err error)
关键操作:mysql.Open(dsn),定义好自己的数据库信息dsn。
gorm.Open(mysql.Open(dsn), &gorm.Config{//详细配置信息 })
var DB *gorm.DB
var mysqlLogger logger.Interfacefunc init() {// 设置日志级别mysqlLogger = logger.Default.LogMode(logger.Info)dsn := "root:root@tcp(127.0.0.1:3306)/gorm?charset=utf8mb4&parseTime=True&loc=Local"db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{// 开启日志Logger: mysqlLogger,})if err != nil {fmt.Println("连接数据库失败" + err.Error())}// 打印数据库连接对象fmt.Println(db)DB = db
}
mysql.Open(dsn),mysql会对dsn进行解析:
func Open(dsn string) gorm.Dialector {dsnConf, _ := mysql.ParseDSN(dsn)return &Dialector{Config: &Config{DSN: dsn, DSNConfig: dsnConf}}
}
&gorm.Config{},添加详细的配置信息:
eg:事务开启,命名转换和连接池等等等
type Config struct {// GORM perform single create, update, delete operations in transactions by default to ensure database data integrity// You can disable it by setting `SkipDefaultTransaction` to trueSkipDefaultTransaction bool// NamingStrategy tables, columns naming strategyNamingStrategy schema.Namer// FullSaveAssociations full save associationsFullSaveAssociations bool// LoggerLogger logger.Interface// NowFunc the function to be used when creating a new timestampNowFunc func() time.Time// DryRun generate sql without executeDryRun bool// PrepareStmt executes the given query in cached statementPrepareStmt bool// DisableAutomaticPingDisableAutomaticPing bool// DisableForeignKeyConstraintWhenMigratingDisableForeignKeyConstraintWhenMigrating bool// IgnoreRelationshipsWhenMigratingIgnoreRelationshipsWhenMigrating bool// DisableNestedTransaction disable nested transactionDisableNestedTransaction bool// AllowGlobalUpdate allow global updateAllowGlobalUpdate bool// QueryFields executes the SQL query with all fields of the tableQueryFields bool// CreateBatchSize default create batch sizeCreateBatchSize int// TranslateError enabling error translationTranslateError bool// PropagateUnscoped propagate Unscoped to every other nested statementPropagateUnscoped bool// ClauseBuilders clause builderClauseBuilders map[string]clause.ClauseBuilder// ConnPool db conn poolConnPool ConnPool// Dialector database dialectorDialector// Plugins registered pluginsPlugins map[string]Plugincallbacks *callbackscacheStore *sync.Map
}
数据库连接池:
sqlDB, _ := DB.DB()// SetMaxIdleConns 设置空闲连接池中的最大连接数。sqlDB.SetMaxIdleConns(10)//SetMaxOpenConns 设置到数据库的最大打开连接数。sqlDB.SetMaxOpenConns(100)// SetConnMaxLifetime 设置可以重复使用连接的最长时间。sqlDB.SetConnMaxLifetime(time.Hour)
4.创建数据库表
DB.AutoMigrate(),根据结构体创建数据库表。
type User struct {ID uint `gorm:"size:10"`Name string `gorm:"size:256"`Age int `gorm:"size:3;default:18"`Email *string `gorm:"size:256;comment:qq邮箱"`UserGender string `gorm:"size:10;default:男"`
}func main() {DB.AutoMigrate(&User{})
}
常用grom字段标签:
标签名 | 说明 |
---|---|
column | 指定 db 列名 |
type | 列数据类型,推荐使用兼容性好的通用类型 |
default | 定义列的默认值 |
size | 定义列数据类型的大小或长度,例如 size: 256 |
comment | 迁移时为字段添加注释 |
结果:
ID字段默认为自增主键;驼峰命名-->蛇形命名
5.CRUD
新增
新增单条记录:
DB.Create(&User{Name: "xiaoming",Age: 18,Email: nil,
})
新增多条记录:
users := []User{{Name: "xiaohong", Age: 18, Email: nil},{Name: "xiaolan", Age: 18, Email: nil},
}
DB.Create(&users)
DB.CreateInBatches(&users, 2)
指定字段新增:
user := &User{Name: "牛人",Age: 20,
}
DB.Select("name").Create(&user)
忽略字段新增:
user := &User{Name: "马人",Age: 20,
}
DB.Omit("name").Create(&user)
查询
查询单条记录:
user := User{}
//查询记录
DB.Take(&user)
//根据主键顺序查询
DB.First(&user)
//根据主键逆序查询
DB.Last(&user)
查询指定字段:
Select
DB.Select("name").Take(&user)
根据主键ID查询:
Take查询单条;Find查询单和多条
user := User{}
//查询主键ID=1的记录
DB.Take(&user, 1)
// SELECT * FROM user WHERE id = 1;
users := []User{}
//根据主键ID查询多条记录
DB.Find(&users, []int{1, 4, 5, 8})
// SELECT * FROM user WHERE id in (1,4,5,8);
根据其他字段查询:
内联条件:
DB.Take(&user, "name = ?", "小明")DB.Find(&users, "name in (?)", []string{"小明", "小红"})
where/not格式:
DB.Where("name = ?", "小明").Take(&user)
DB.Not("name = ?", "小明").Take(&user)
DB.Where("name in (?)", []string{"小明", "小红"}).Find(&users)
Or:
DB.Where("name like ?", "%明").Or("age>?", 18).Find(&user)
Struct&map条件:
// Struct
db.Where(&User{Name: "小明", Age: 20}).First(&user)
// SELECT * FROM users WHERE name = "小明" AND age = 20 ORDER BY id LIMIT 1;// Map
db.Where(map[string]interface{}{"name": "小红", "age": 20}).Find(&users)
// SELECT * FROM users WHERE name = "小红" AND age = 20;
分页limit和排序order :
users := []User{}
//分页
DB.Model(&User{}).Limit(2).Offset(0).Find(&users)
// SELECT * FROM users OFFSET 0 LIMIT 2;
//排序
DB.Order("age desc").Find(&users)
扫描scan:
字段转移,只保留需要的字段到对象
type UserInfo struct {ID uint Name string
}
userInfo := []UserInfo{}
DB.Model(&User{}).Where("name like ?", "%xiao").Scan(&userInfo)
去重distinct :
//去重
a := []int{}
DB.Model(&User{}).Distinct("age").Scan(&a)
//DB.Model(&User{}).Select("distinct age").Scan(&a)
fmt.Println(a)
分组group:
type man struct {NameList stringCount intGender string
}
//分组Group
m := []man{}
DB.Model(&User{}).Select("count(id) as count", "gender").Group("gender").Scan(&m)//使用分行形式
DB.Model(&User{}).Select("group_concat(name) as name_list","count(id) as count","gender").Group("gender").Scan(&m)//原生SQL
DB.
Raw("select group_concat(name) as name_list,count(id) as count,gender from users group by gender").
Scan(&m)
子查询:
type man struct {NameList stringName stringCount intGender string
}
m := []man{}
// 错误,DB.Select("avg(age)").Find(&User{})子查询不能使用Find,使用Model指定表名
DB.Model(&User{}).Where("age>(?)", DB.Model(&User{}).Select("avg(age)")).Scan(&m)
命名参数/find至map/scopes :
1.使用sql.Named
2.查询结果返回到map中
3.定义scope函数(参数 *gorm.DB,返回值 *gorm.DB
//命名参数@u := []User{}DB.Where("name = @name and age = @age", sql.Named("name", "小小"), sql.Named("age", 18)).Find(&u)//map参数DB.Where("name = @name and age = @age",map[string]any{"name": "小小", "age": 18}).Find(&u)fmt.Println(u)//find至mapm2 := []map[string]interface{}{}//DB.Model(&User{}).Where("age > ?", 18).Find(&m2)//fmt.Println(m2)//scopeDB.Model(&User{}).Scopes(GenderIsMan).Where("age > ?", 18).Find(&m2)fmt.Println(m2)
}
func GenderIsMan(db *gorm.DB) *gorm.DB {return db.Where("gender = ?", "男")
}
Joins连接:
连接两张表
type result struct {Name stringEmail string
}db.Model(&User{}).Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Scan(&result{})
// SELECT users.name, emails.email FROM `users` left join emails on emails.user_id = users.id
更新
更新单行记录:
Save();指定更新字段Select()
user := User{}
DB.Take(&user)
user.Name = "dajizhong"
user.Age = 20
//1.更新单行记录
DB.Save(&user)
//1.2更新指定字段
DB.Select("age").Save(&user)
更新多行记录
单个字段Update()
//2.更新多行记录
users := []User{}
DB.Find(&users, []int{1, 2, 3, 4, 5}).Update("email", "222@qq.com")
DB.Find(&users, []int{1, 2, 3, 4}).Update("age", "38")
多个字段Updates()
//2.2 使用结构体更新
DB.Find(&users, []int{1, 2, 3, 4}).Updates(User{Age: 59,
})
//2.3 使用map更新
DB.Find(&users, []int{1, 2, 3, 4}).Updates(map[string]interface{}{"email": "333@qq.com","age": 38,
})
删除:
DB.Delete(&User{}, 10)
DB.Delete(&User{}, []int{1, 2, 3, 4, 5, 6, 7, 8})
DB.Delete(&User{}, "age = ?", 18)
6.Hook
Hook 是在创建、查询、更新、删除等操作之前、之后调用的函数。
如果您已经为模型定义了指定的方法,它会在创建、更新、查询、删除时自动被调用。如果任何回调返回错误,GORM 将停止后续的操作并回滚事务。
钩子方法的函数签名应该是 func(*gorm.DB) error
创建可用Hook:BeforeCreate/AfterCreate
// BeforeCreate 钩子函数
func (u *User) BeforeCreate(tx *gorm.DB) (err error) {email := "3333@qq.com"u.Email = &emailreturn
}
// AfterCreate 钩子函数
func (u *User) AfterCreate(tx *gorm.DB) (err error) {if u.ID == 1 {tx.Model(u).Update("role", "admin")}return
}
更新可用Hook:BeforeUpdate/AfterUpdate
删除可用Hook:BeforeCreate/AfterCreate
查询可用Hook:AfterFind