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 models
import (
"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.DB
type 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 error
var 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 = db
casbin.InitCasbin(db)
}
// addExtraSpaceIfExist adds a separator
func addExtraSpaceIfExist(str string) string {
if str != "" {
return " " + str
}
return ""
}
package conf
type 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.Time
UpdatedAt time.Time
DeletedAt 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.Model
Name 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 func
FirstName string
LastName string
Age uint8
FullName 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.ErrMissingWhereClause
db.Model(&User{}).Where("1 = 1").Update("name", "jinzhu")
// UPDATE users SET `name` = "jinzhu" WHERE 1=1
db.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.ErrMissingWhereClause
db.Where("1 = 1").Delete(&User{})
// DELETE FROM `users` WHERE 1=1
db.Exec("DELETE FROM users")
// DELETE FROM users
db.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 uint
Name 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
}