GORM-Golang的ORM框架(1)

发布于:2025-04-10 ⋅ 阅读:(38) ⋅ 点赞:(0)

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.Interface

func 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 true
	SkipDefaultTransaction bool
	// NamingStrategy tables, columns naming strategy
	NamingStrategy schema.Namer
	// FullSaveAssociations full save associations
	FullSaveAssociations bool
	// Logger
	Logger logger.Interface
	// NowFunc the function to be used when creating a new timestamp
	NowFunc func() time.Time
	// DryRun generate sql without execute
	DryRun bool
	// PrepareStmt executes the given query in cached statement
	PrepareStmt bool
	// DisableAutomaticPing
	DisableAutomaticPing bool
	// DisableForeignKeyConstraintWhenMigrating
	DisableForeignKeyConstraintWhenMigrating bool
	// IgnoreRelationshipsWhenMigrating
	IgnoreRelationshipsWhenMigrating bool
	// DisableNestedTransaction disable nested transaction
	DisableNestedTransaction bool
	// AllowGlobalUpdate allow global update
	AllowGlobalUpdate bool
	// QueryFields executes the SQL query with all fields of the table
	QueryFields bool
	// CreateBatchSize default create batch size
	CreateBatchSize int
	// TranslateError enabling error translation
	TranslateError bool
	// PropagateUnscoped propagate Unscoped to every other nested statement
	PropagateUnscoped bool

	// ClauseBuilders clause builder
	ClauseBuilders map[string]clause.ClauseBuilder
	// ConnPool db conn pool
	ConnPool ConnPool
	// Dialector database dialector
	Dialector
	// Plugins registered plugins
	Plugins map[string]Plugin

	callbacks  *callbacks
	cacheStore *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 string
    Count    int
    Gender   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 string
    Name     string
    Count    int
    Gender   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至map
    m2 := []map[string]interface{}{}
    //DB.Model(&User{}).Where("age > ?", 18).Find(&m2)
    //fmt.Println(m2)
    //scope
    DB.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  string
  Email 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 = &email
	return
}
// 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


网站公告

今日签到

点亮在社区的每一天
去签到