Gorm 是 Golang 的一个 orm 框架。ORM 是通过实例对象的语法,完成关系型 数据库的操作,是"对象-关系映射"(Object/Relational Mapping) 的缩写。使用 ORM 框架可以让我们更方便的操作数据库。
Gorm官方支持的数据库类型有: MySQL, PostgreSQL, SQlite, SQL Server
国产的go-orm框架,中文文档齐全,对开发者友好,支持主流数据库。
优点:
## 安装好go环境之后用go命令安装gorm
go get -u gorm.io/gorm
## 安装你要使用的相应数据库驱动。这里以MySQL驱动为例,GORM 官方支持的数据库类型有: MySQL, PostgreSQL, SQlite, SQL Server
go get -u gorm.io/driver/mysql
package modelsimport ("fmt""gorm.io/driver/mysql""gorm.io/gorm""gorm.io/gorm/logger""gorm.io/plugin/soft_delete""yixiang.co/go-mall/pkg/global"//"gorm.io/plugin/soft_delete""log""os""time""yixiang.co/go-mall/pkg/casbin"
)var db *gorm.DBtype BaseModel struct {Id int64 `gorm:"primary_key" json:"id"`UpdateTime time.Time `json:"updateTime" gorm:"autoUpdateTime"`CreateTime time.Time `json:"createTime" gorm:"autoCreateTime"`IsDel soft_delete.DeletedAt `json:"isDel" gorm:"softDelete:flag"`
}// Setup initializes the database instance
func Setup() {var err errorvar connStr = fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8&parseTime=True&loc=Local",global.YSHOP_CONFIG.Database.User,global.YSHOP_CONFIG.Database.Password,global.YSHOP_CONFIG.Database.Host,global.YSHOP_CONFIG.Database.Name)newLogger := logger.New(log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer(日志输出的目标,前缀和日志包含的内容——译者注)logger.Config{SlowThreshold: time.Second, // 慢 SQL 阈值LogLevel: logger.Info, // 日志级别IgnoreRecordNotFoundError: true, // 忽略ErrRecordNotFound(记录未找到)错误Colorful: true, // 禁用彩色打印},)db, err = gorm.Open(mysql.Open(connStr), &gorm.Config{Logger: newLogger,})if err != nil {log.Printf("[info] gorm %s", err)}sqlDB, err := db.DB()if err != nil {log.Printf("[info] gorm %s", err)}// SetMaxIdleConns 设置空闲连接池中连接的最大数量sqlDB.SetMaxIdleConns(10)// SetMaxOpenConns 设置打开数据库连接的最大数量。sqlDB.SetMaxOpenConns(100)// SetConnMaxLifetime 设置了连接可复用的最大时间。sqlDB.SetConnMaxLifetime(time.Hour)global.YSHOP_DB = dbcasbin.InitCasbin(db)}
// addExtraSpaceIfExist adds a separator
func addExtraSpaceIfExist(str string) string {if str != "" {return " " + str}return ""
}
package conftype Database struct {Type string `mapstructure:"type" yaml:"type"`User string `mapstructure:"user" yaml:"user"`Password string `mapstructure:"password" yaml:"password"`Host string `mapstructure:"host" yaml:"host"`Name string `mapstructure:"name" yaml:"name"`TablePrefix string `mapstructure:"table-prefix" yaml:"table-prefix"`
}
yml文件:
database:type: 'mysql'user: 'root'password: 'root'host: '127.0.0.1:3306'name: 'yshop_go'table_prefix:
默认情况下,GORM 使用 ID 作为主键,使用结构体名的 蛇形复数 作为表名,字段名的 蛇形 作为列名,并使用 CreatedAt、UpdatedAt 字段追踪创建、更新时间。
遵循 GORM 已有的约定,可以减少配置和代码量。如果约定不符合需求,GORM 允许自定义配置它们。
type User struct {
// 默认情况下,名为 `ID` 的字段会作为表的主键 Name string
ID string
}
例子:
struct: userinfo 默认表名:user_infos
// 根据 User 的字段创建 `deleted_users` 表
db.Table("deleted_users").AutoMigrate(&User{})// 从另一张表查询数据
var deletedUsers []User
db.Table("deleted_users").Find(&deletedUsers)
// SELECT * FROM deleted_users;db.Table("deleted_users").Where("name = ?", "jinzhu").Delete(&User{})
// DELETE FROM deleted_users WHERE name = 'jinzhu';
GORM 定义一个 gorm.Model 结构体,其包括字段 ID、CreatedAt、UpdatedAt、DeletedAt。
// gorm.Model 的定义
type Model struct {ID uint `gorm:"primaryKey"`CreatedAt time.TimeUpdatedAt time.TimeDeletedAt gorm.DeletedAt `gorm:"index"`
}
// -----------------------------插入单条记录---------------------------------
user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}result := db.Create(&user) // 通过数据的指针来创建user.ID // 返回插入数据的主键
result.Error // 返回 error
result.RowsAffected // 返回插入记录的条数// 创建记录并更新给出的字段
db.Select("Name", "Age", "CreatedAt").Create(&user)
// INSERT INTO `users` (`name`,`age`,`created_at`) VALUES ("jinzhu", 18, "2020-07-04 11:05:21.775")
// -----------------------------批量插入多条记录-----------------------------
//要有效地插入大量记录,请将一个 slice 传递给 Create 方法。 GORM 将生成单独一条SQL语句来插入所有数据,并回填主键的值,钩子方法也会被调用。
var users = []User{{Name: "jinzhu1"}, {Name: "jinzhu2"}, {Name: "jinzhu3"}}
db.Create(&users)//使用 CreateInBatches 分批创建时,你可以指定每批的数量。
var users = []User{{name: "jinzhu_1"}, ...., {Name: "jinzhu_10000"}}
// 数量为 100
db.CreateInBatches(users, 100)
func (u *User) BeforeCreate(tx *gorm.DB) (err error) {u.UUID = uuid.New()if u.Role == "admin" {return errors.New("invalid role")}return
}
// 如果想跳过 钩子 方法,可以使用 SkipHooks 会话模式
DB.Session(&gorm.Session{SkipHooks: true}).Create(&user)
DB.Session(&gorm.Session{SkipHooks: true}).Create(&users)
DB.Session(&gorm.Session{SkipHooks: true}).CreateInBatches(users, 100)
db.Model(&User{}).Create(map[string]interface{}{"Name": "jinzhu", "Age": 18,
})// batch insert from `[]map[string]interface{}{}`
db.Model(&User{}).Create([]map[string]interface{}{{"Name": "jinzhu_1", "Age": 18},{"Name": "jinzhu_2", "Age": 20},
})
对于声明了默认值的字段,像 0、‘’、false 等零值是不会保存到数据库。需要使用指针类。
type User struct {gorm.ModelName string `gorm:"default:xiaoming"`Age *int `gorm:"default:18"`Active sql.NullBool `gorm:"default:true"`
}type User struct {ID string `gorm:"default:uuid_generate_v3()"` // db funcFirstName stringLastName stringAge uint8FullName string `gorm:"->;type:GENERATED ALWAYS AS (concat(firstname,' ',lastname));default:(-);"`
}
//获取第一条记录(主键升序),只有在目标struct是指针或者通过db.Model()指定model时,该方法才有效
db.First(&user)
SELECT * FROM users ORDER BY id LIMIT 1;//获取第一条记录,没有指定排序字段
db.Take(&user)
SELECT * FROM users LIMIT 1;//获取最后一条记录(主键降序)
db.Last(&user)
SELECT * FROM users ORDER BY id DESC LIMIT 1;result:=db.First(&user)
result.RowsAffected //找到返回的记录数
result.Error //return error or nil//检查ErrRecordNotFound错误
errors.Is(result.Error,gorm.ErrRecordNotFound)
db.First(&user,10)
SELECT * FROM users WHERE id=10;db.First(&user,"10")
SELECT * FROM users WHERE id=10;db.Find(&user,[]int{1,2,3})
SELECT * FROM users WHERE id IN (1,2,3);db.First(&user,"id=?","123456")
SELECT * FROM users WHERE id="123456"
//得到全部记录
result:=db.Find(&user)
SELECT * FROM users;
func(u *User) AfterFind(tx *gorm.DB)(err error){
if u.MemberShip ==""{
u.MemberShip="user"
}
return
}
//查询正序第一条条件匹配记录
db.Where("name = ?","zhangsan").First(&user)
SELECT * FROM users WHERE name='zhangsan' ORDER BY id LIMIT 1;//查询能满足条件匹配的所有记录
db.Where("name <> ?","zhangsan").Find(&user)
SELECT * FROM users WHERE name <> 'zhangsan';//in用法 查询名字为张三,张三1的所有记录
db.Where("name IN ?",[]string{"zhangsan","zhangsan1"}).Find(&user)
SELECT * FROM users WHERE name IN ('zhangsan','zhangsan1');//like用法 根据zhang姓模糊查询
db.Where("name LIKE ?","%zhang%").Find(&user)
SELECT * FROM users WHERE name LIKE '%zhang%';//and用法
db.Where("name = ? AND age >= ?","zhngsan","20").Find(&user)
SELECT * FROM users WHERE name='zhangsan' AND age >=20;//DateTime用法
db.Where("updated_at > ?", lastWeek).Find(&users)
SELECT * FROM users WHERE updated_at > '2000-01-01 00:00:00';//between用法
db.Where("created_at BETWEEN ? AND ?",lastWeek,today).Find(&user)
SELECT * FROM user WHERE created_at BETWEEN '2000-01-01 00:00:00' AND '2000-01-08 00:00:00';
//筛选name和age字段
db.Select("name","age").Find(&user)
SELECT name,age FROM user;
db.Select([]string{"name","age"}).Find(&user)
SELECT name,age FROM user;//sql函数使用
db.Table("users").Select("COALESCE(age,?)",42).Rows()
SELECT COALESCE(age,'42') FROM users;
//struct
db.Where(&User{Name:"zhangsan",Age:20}).First(&user)
SELECT * FROM users WHERE name="zhangsan" AND age=20 ORDER BY id LIMIT 1;//map
db.Where(map[string]interface{}{"name":"zhangsan","age":20}).Find(&user)
SELECT * FROM users WHERE name="zhangsan" AND age=20;//传入主键切片
db.Where([]int64{1,2,3}).Find(&user)
SELECT * FROM users WHERE id IN(1,2,3);
// Get by primary key if it were a non-integer type
db.First(&user, "id = ?", "string_primary_key")
SELECT * FROM users WHERE id = 'string_primary_key';// Plain SQL
db.Find(&user, "name = ?", "jinzhu")
SELECT * FROM users WHERE name = "jinzhu";//
db.Find(&users,"name <> ? AND age > ?","bbQ",20)
SELECT * FROM users WHERE name <> "bbQ" AND age > 20;//struct
db.Find(&users,User{Age:20})
SELECT * FROM users WHERE age=20;//map
db.Find(&users,map[string]interface{}{"age":20})
SELECT * FROM users WHERE age=20;
db.Not("name = ?", "jinzhu").First(&user)
// SELECT * FROM users WHERE NOT name = "jinzhu" ORDER BY id LIMIT 1;// Not In
db.Not(map[string]interface{}{"name": []string{"jinzhu", "jinzhu 2"}}).Find(&users)
// SELECT * FROM users WHERE name NOT IN ("jinzhu", "jinzhu 2");// Struct
db.Not(User{Name: "jinzhu", Age: 18}).First(&user)
// SELECT * FROM users WHERE name <> "jinzhu" AND age <> 18 ORDER BY id LIMIT 1;// Not In slice of primary keys
db.Not([]int64{1,2,3}).First(&user)
// SELECT * FROM users WHERE id NOT IN (1,2,3) ORDER BY id LIMIT 1;
db.Where("role = ?","admin").Or("role = ?","super_admin").Find(&users)
SELECT * FROM users WHERE role='admin' OR role='super_admin';//struct
db.Where("name='zhangsan'").Or(User{Name:"zhangsan",Age:18}).Find(&users)
SELECT * FROM users WHERE name='zhangsan' OR (name='zhangsan' AND age=18);//map
db.Where("name = 'jinzhu'").Or(map[string]interface{}{"name": "jinzhu 2", "age": 18}).Find(&users)
SELECT * FROM users WHERE name = 'jinzhu' OR (name = 'jinzhu 2' AND age = 18);
db.Order("age desc,name").Find(&users)
SELECT * FROM users ORDER BY age DESC,name;db.Order("age desc").Order("name").Find(&users)
SELECT * FROM users ORDER BY age DESC,name;db.Clauses(clause.OrderBy{Expression: clause.Expr{SQL: "FIELD(id,?)", Vars: []interface{}{[]int{1, 2, 3}}, WithoutParentheses: true},
}).Find(&User{})
SELECT * FROM users ORDER BY FIELD(id,1,2,3)
db.Limit(3).Find(&users)
SELECT * FROM users Limits 3;db.Limit(10).Find(&users1).Limit(-1).Find(&users2)
SELECT * FROM users Limit 10;(users1)
SELECT * FROM users ;(users2)db.Offset(3).Find(&users)
SELECT * FROM users OFFSET 3;//分页第五页取十条记录
db.Limit(10).Offset(5).Find(&users)
SELECT * FROM users OFFSET 5 LIMIT 10;
//
db.Model(&User{}).Select("name,sum(age) as total").Where("name LIKE ?","group%").Group("name").First(&result)
SELECT name,sum(age) as total FROM users WHERE name like "group%" GROUP BY name Limit 1;db.Model(&User{}).Select("name, sum(age) as total").Group("name").Having("name = ?", "group").Find(&result)
SELECT name, sum(age) as total FROM `users` GROUP BY `name` HAVING name = "group"
db.Distinct("name", "age").Order("name, age desc").Find(&results)
type Result struct{
Name string
Age int
}
var result Result
db.Table("users").Select("name", "age").Where("name = ?", "Antonio").Scan(&result)// Raw SQL
db.Raw("SELECT name, age FROM users WHERE name = ?", "Antonio").Scan(&result)
//-----------------------------保存所有字段-------------------------
db.First(&user)
user.Name = "jinzhu 2"
user.Age = 100
db.Save(&user)
UPDATE users SET name='jinzhu 2', age=100, birthday='2016-01-01', updated_at = '2013-11-17 21:34:10' WHERE id=111;//-----------------------------更新单个列-------------------------
// 条件更新
db.Model(&User{}).Where("active = ?", true).Update("name", "hello")
UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE active=true;// User 的 ID 是 `111`
db.Model(&user).Update("name", "hello")
UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111;// 根据条件和 model 的值进行更新
db.Model(&user).Where("active = ?", true).Update("name", "hello")
UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111 AND active=true;//-----------------------------更新多列-------------------------
// 根据 `struct` 更新属性,只会更新非零值的字段
db.Model(&user).Updates(User{Name: "hello", Age: 18, Active: false})
UPDATE users SET name='hello', age=18, updated_at = '2013-11-17 21:34:10' WHERE id = 111;// 根据 `map` 更新属性
db.Model(&user).Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
// UPDATE users SET name='hello', age=18, active=false, updated_at='2013-11-17 21:34:10' WHERE id=111;//-----------------------------更新选定字段-------------------------
// 使用 Map 进行 Select
// User's ID is `111`:
db.Model(&user).Select("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
UPDATE users SET name='hello' WHERE id=111;db.Model(&user).Omit("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
UPDATE users SET age=18, active=false, updated_at='2013-11-17 21:34:10' WHERE id=111;// 使用 Struct 进行 Select(会 select 零值的字段)
db.Model(&user).Select("Name", "Age").Updates(User{Name: "new_name", Age: 0})
UPDATE users SET name='new_name', age=0 WHERE id=111;// Select 所有字段(查询包括零值字段的所有字段)
db.Model(&user).Select("*").Update(User{Name: "jinzhu", Role: "admin", Age: 0})// Select 除 Role 外的所有字段(包括零值字段的所有字段)
db.Model(&user).Select("*").Omit("Role").Update(User{Name: "jinzhu", Role: "admin", Age: 0})
//-----------------------------批量更新-------------------------
// 根据 struct 更新
db.Model(User{}).Where("role = ?", "admin").Updates(User{Name: "hello", Age: 18})
// UPDATE users SET name='hello', age=18 WHERE role = 'admin';// 根据 map 更新
db.Table("users").Where("id IN ?", []int{10, 11}).Updates(map[string]interface{}{"name": "hello", "age": 18})
// UPDATE users SET name='hello', age=18 WHERE id IN (10, 11);//-----------------------------禁止全局更新-------------------------
//如果在没有任何条件的情况下执行批量更新,默认情况下,GORM 不会执行该操作,并返回 ErrMissingWhereClause 错误。对此,必须加一些条件,或者使用原生 SQL,或者启用 AllowGlobalUpdate 模式
db.Model(&User{}).Update("name", "jinzhu").Error // gorm.ErrMissingWhereClausedb.Model(&User{}).Where("1 = 1").Update("name", "jinzhu")
// UPDATE users SET `name` = "jinzhu" WHERE 1=1db.Exec("UPDATE users SET name = ?", "jinzhu")
// UPDATE users SET name = "jinzhu"db.Session(&gorm.Session{AllowGlobalUpdate: true}).Model(&User{}).Update("name", "jinzhu")
// UPDATE users SET `name` = "jinzhu"//-----------------------------更新记录数-------------------------
// 通过 `RowsAffected` 得到更新的记录数
result := db.Model(User{}).Where("role = ?", "admin").Updates(User{Name: "hello", Age: 18})
// UPDATE users SET name='hello', age=18 WHERE role = 'admin';result.RowsAffected // 更新的记录数
result.Error // 更新的错误
//-----------------------------更新 Hook-------------------------
//GORM 支持 BeforeSave、BeforeUpdate、AfterSave、AfterUpdate 钩子,这些方法将在更新记录时被调用
func (u *User) BeforeUpdate(tx *gorm.DB) (err error) {if u.Role == "admin" {return errors.New("admin user not allowed to update")}return
}
//-----------------------------删除一条记录-------------------------
//删除一条记录时,删除对象需要指定主键,否则会触发 批量 Delete
// Email 的 ID 是 `10`
db.Delete(&email)
// DELETE from emails where id = 10;// 带额外条件的删除
db.Where("name = ?", "jinzhu").Delete(&email)
// DELETE from emails where id = 10 AND name = "jinzhu";//-----------------------------根据主键删除-------------------------
db.Delete(&User{}, 10)
// DELETE FROM users WHERE id = 10;db.Delete(&User{}, "10")
// DELETE FROM users WHERE id = 10;db.Delete(&users, []int{1,2,3})
// DELETE FROM users WHERE id IN (1,2,3);
//-----------------------------批量删除-------------------------
db.Where("email LIKE ?", "%jinzhu%").Delete(&Email{})
// DELETE from emails where email LIKE "%jinzhu%";db.Delete(&Email{}, "email LIKE ?", "%jinzhu%")
// DELETE from emails where email LIKE "%jinzhu%";//-----------------------------禁止全局删除-------------------------
//如果在没有任何条件的情况下执行批量删除,GORM 不会执行该操作,并返回 ErrMissingWhereClause 错误。对此,你必须加一些条件,或者使用原生 SQL,或者启用 AllowGlobalUpdate 模式
db.Delete(&User{}).Error // gorm.ErrMissingWhereClausedb.Where("1 = 1").Delete(&User{})
// DELETE FROM `users` WHERE 1=1db.Exec("DELETE FROM users")
// DELETE FROM usersdb.Session(&gorm.Session{AllowGlobalUpdate: true}).Delete(&User{})
// DELETE FROM users
//-----------------------------软删除-------------------------
//By default, gorm.Model uses *time.Time as the value for the DeletedAt field, and it provides other data formats support with plugin gorm.io/plugin/soft_delete
import "gorm.io/plugin/soft_delete"
type User struct {ID uintName string `gorm:"uniqueIndex:udx_name"`DeletedAt soft_delete.DeletedAt `gorm:"uniqueIndex:udx_name"`// DeletedAt soft_delete.DeletedAt `gorm:"softDelete:nano"`//IsDel soft_delete.DeletedAt `gorm:"softDelete:flag"`
}// Query
SELECT * FROM users WHERE is_del = 0;// Delete
UPDATE users SET is_del = 1 WHERE ID = 1;// user 的 ID 是 `111`
db.Delete(&user)
// UPDATE users SET deleted_at="2013-10-29 10:23" WHERE id = 111;// 批量删除
db.Where("age = ?", 20).Delete(&User{})
// UPDATE users SET deleted_at="2013-10-29 10:23" WHERE age = 20;// 在查询时会忽略被软删除的记录
db.Where("age = 20").Find(&user)
// SELECT * FROM users WHERE age = 20 AND deleted_at IS NULL;//-----------------------------查找被软删除的记录-------------------------
db.Unscoped().Where("age = 20").Find(&users)
// SELECT * FROM users WHERE age = 20;//-----------------------------永久删除-------------------------
db.Unscoped().Delete(&order)
// DELETE FROM orders WHERE id=10;
//-----------------------------删除 Hook-------------------------
func (u *User) BeforeDelete(tx *gorm.DB) (err error) {if u.Role == "admin" {return errors.New("admin user not allowed to delete")}return
}
type Result struct{
ID int
Name string
Age int
}
var result Result
db.Raw("SELECT id,name,age From users WHERE name=?",3).Scan(&result)var ages int
db.Raw("SELECT SUM(age) FROM users WHERE role = ?","admin").Scan(&ages)var users []User
db.Raw("UPDATE users SET name = ? WHERE age = ? RETURNING id,name","zhangsan",20).Scan(&users)
db.Exec("DROP TABLE users")
db.Exec("UPDATE orders SET shipped_at = ? WHERE id IN ?",time.Now(),[]int64{1,2,3})db.Exec("UPDATE users SET money = ? WHERE name =?",gorm.Expr("money * ? + ?",10000,1),"zhangsan")
//获取 *sql.Row结果
//使用gorm api 构建sql
row :=db.Table("users").Where("name = ?","zhangsan").Select("name","age").Row()
row.Scan(&name,&age)//使用原生sql
row:=db.Raw("SELECT name ,age ,email FROM users WHERE name=?","zhangsan").Row()
row.Scan(&name,&age,&email)rows,err:=db.Raw("SELECT name,age,email FROM users WHERE name=?","zhangsan").Rows()
defer rows.Close()
for rows.Next(){
rows.Scan(&name,&age,&email)
//crud
}//
rows,err:=db.Model(&User{}).Where("name = ?","zhangsan").Select("name,age,email").Rows()
defer rows.Close()
var user User
for rows.Next(){
//ScanRows 将一行扫描至user
db.ScanRows(rows,&user)
//crud
}