GORM小结
orm是什么
ORM:对象关系映射(Object Relational Mapping),目的是想像操作对象一样操作数据库,因为数据库不是面向对象的,所以需要编程进行映射,简单的来说,我们通过ORM来处理数据库操作会比原生更简单,部分操作也会更快捷,当然,某些精确的操作还是原生数据库语句比较方便。
一些SQL语句需要注意的点
-
MySQL在windows下不区分大小写,但是在linux下是区分大小写的,所以我们通常把数据库名,表名,列名都小写
-
SQL语句最好是使用单引号,其实单双引号都能正确跑出来,但是其实双引号会被解释器翻译成单引号
-
MySQL官方是建议使用<>符号而不是!=
GORM的使用
官方demo
func main() {
db, err := gorm.Open("mysql", "root:cxs20030416@(127.0.0.1:3306)/sql_test?charset=utf8&parseTime=True&loc=Local")
if err != nil {
panic("failed to connect database")
}
defer db.Close()
//自动检查 Product 结构是否变化,变化则进行迁移
db.AutoMigrate(&Product{})
// 增
db.Create(&Product{Code: "L1212", Price: 1000})
// 查
var product Product
db.First(&product, 1) // 找到id为1的产品
db.First(&product, "code = ?", "L1212") // 找出 code 为 l1212 的产品
// 改 - 更新产品的价格为 2000
db.Model(&product).Update("Price", 2000)
// 删 - 删除产品
db.Delete(&product)
}
模型定义
模型一般都是普通的Golang的结构体,Go的基本数据类型,或者指针,同时也支持接口
type User struct{
gorm.Model
Name string
Age sql.NullINt64 //当没有对应的数据时 会出现rows.Scan(nil)
Birthday *time.Time
Email string `gorm::"type:varchar(100);unique_index" //唯一索引
Role string `gorm:"size:255"` //设置字段的大小为255个字节
MemberNumber *string `gorm:"unique;not null"` //设置memberNumber字段唯一且不为空
Num int `gorm:"AUTO_INCREMENT" //设置num字段自增
Address string `gorm:"index:addr" ` //给Address创建一个名字是addr的索引
IgnoreMe int `gorm:"-"` //忽略这个字段
}
结构标签
模型惯例
gorm.Model
gorm.Model是一个包含一些基本字段的结构体,包含的字段有ID,CreatedAt,UpdateAt,DeleteAt可以吧这个模型嵌入到你的模型中,默认使用ID作为主键名
复数表名和蛇形列名
GORM表名是结构体名称的复数形式,列名是字段名的蛇形小写形式。
//指定表名
//用User结构体创建delete_users表
db.Table("deleted_users").CreateTable(&User{})
type User struct{
ID uint //字段名 id
Name string //字段名name
Birthday time.Time //birthday
CreatedAt time.Time //created_at
}
//重写列名
type Animal struct{
AnimalId int76 `gorm:"column:beast_id"` //设置列名为beast_id
}
时间戳跟踪
CreatedAt
对于有该字段的模型,它被设置为首次创建记录的当前时间
db.Create(&user) //将设置“CreateAt” 为当前时间
UpdatedAt
对于有该字段的模型,它将被设置为记录更新时的当前时间
db.Save(&user) //将设置“UpdatedAt”为当前时间
DeletedAt
对于有该字段的模型,当删除它们的示例时,它们并没有被从数据库中删除,只是将DeletedAt字段设置为当前时间
连接数据库
支持四种数据库
import _ "github.com/jinzhu/gorm/dialects/mysql"
// import _ "github.com/jinzhu/gorm/dialects/postgres"
// import _ "github.com/jinzhu/gorm/dialects/sqlite"
// import _ "github.com/jinzhu/gorm/dialects/mssql"
//MySQL数据库连接示例
func main() {
db, err := gorm.Open("mysql", "root:cxs20030416@(localhost)/sql_test?charset=utf8mb4&parseTime=True&loc=Local")
if err!=nil {
fmt.Println("failed to open database,err:",err)
return
}
fmt.Println("ok")
defer db.Close()
}
简单的增删改查
Create
创建记录
user:=User{Name:"Jinzhu",Age:18,Birthday:time.Now()}
db.NewRecord(user) // 返回true 因为主键为空
db.Create(&user) //创建表
db.NewRecord(user) //在user创建之后返回false
注意:所有包含零值的字段,像0 ' ' false或者其他的零值不会被保存到数据库中,但会使用这个字段的默认值。
Retrieve
// 获取第一条记录,按照主键排序db.First(&user) //获取一条记录,不指定排序db.Take(&user)//获取最后一条记录,按照主键排序db.Last(&user)//获取所有的记录db.Find(&users)//通过主键进行查询db.First(&user,10)
Where
//获取第一条匹配的记录
db.Where("name=?","xiaohuang").First(&user)
//获取所有的匹配记录
db.Where("name=?","xiaohuang").Find(&user)
//<>
db.Where("name <> ?","xiaohuang").Find(&users)
//IN
db.Where("name in ?",[]string{"xiaohuang","xiaocheng"}).Find(&users)
//LIKE
db.Where(""name LIKE ?","%huan%").Find(&users)
//AND
db.Where("name=?ANDage>=?","jinzhu","22").FInd(&users)
//Time
db.Where("updated_at>?",lastWeek).Find(&users)
//BETWEEN
db.Where("created_at BETWEEN ? AND ?",lastwwek,today),Find(&users)
Struct&Map&Slice
//Struct
db.Where(&User{Name:"xiaocheng",Age:18}).First(&user)
//select * from user where name="xiaocheng" and age=18 limit 1
//Map
db.Where(map[string]interface{}{"name":"xiaocheng","age":18}).Find(&user)
//select * from user where name="xiaocheng" and age=18
//slice查询
db.Where([]int64(20,21,22)).Find(&users)
//select * from users where id in(20,21,22)
Not
//与where类似
db.Not("name","jinzhu").Find(&user)
//select * from user where name <> "jinzhu"
Or
db.Where("role=?","admin").Or("role=?","super_admin").Find(&users)
//select * from users where role='admin' or role='super_admin'
行内条件查询
//通过主键进行查询
db.First(&user,23)
//select * from user where name='xiaocheng'
db.Find(&user,"name=?","xiaocheng")
Update
Save方法在执行SQL更新操作时将包含所有字段,即使这些字段没有被修改
db.First(*user)
user.Name="xiaocheng2"
user.Age=100
db.Save(&user)
更新已经更改的字段
db.Model(&user).Update("name","hello")
//Update users set name=“hello”
db.Model(&user).Where("id=?",3).Update("name","hello")
Delete
当删除一条记录的时候,你需要确定这条记录的主键有值,GORM会使用主键来删除这条记录,如果主键字段为空,GORM会删除模型中所有的记录
//删除一条存在的记录
db.Delete(&email,10)
//DELETE from emails where id=10
//批量删除
db.Delete(Email{},"email LIKE ?","%jinzhu%")
//DELETE from emails where email like %jinzhu%
软删除
//如果模型中有DELETEAT字段,它将拥有软删除的能力,当执行删除操作时,数据并不会永久的从数据库中删除,而是将deleteAt的值更新为当前时间
db.Delete(&user,111)
//// UPDATE users SET deleted_at="2013-10-29 10:23" WHERE id = 111;
钩子函数、事物、连接池
链式操作,Gorm继承了链式操作接口
创建方法:指的是那些产生SQL查询并且发送到数据库,通常它就是一些CRUD方法,就像
Create First Find Take Save Updatexxx Delete Scan Row Rows
当使用GORM的创建方法,后面的创建方法将复用前面的创建方法的搜索条件
db.Where("name LIKE ?","jinzhu%").Find(&users," id IN(?)",[]int{1,2,3}).Count(&count)
//select * from users where name like "jinzhu%" and id in(1,2,3)
//select count(*) from users where name like "jinzhu%"
线程安全:
所有的链式操作都将会克隆并创建一个新的数据库对象(共享一个连接池),GORM对于多个goroutines的并发使用是安全的
错误处理:
Go语言中鼓励人们在任何创建方法之后去检查错误
由于GORM的链式API GORM中的错误处理与管用的GO代码不同,但它仍然相当容易
如果发生任何错误,GORM会将其设置为*gorm.DB的ERROR字段,可以这么检查
if err := db.Where("name = ?", "jinzhu").First(&user).Error; err != nil {
// error handling...
}
钩子:当一个事件触发的时候,在系统级捕获到了它,然后做一些操作,一段用以处理系统消息的程序
钩子就是在某个阶段给你一个做某些处理的机会
如果你在一个模型中定义了特殊的方法,它将会在插入,更新,查询,删除的时候被自动调用,如果任何的回调抛出错误,GORM 将会停止将要执行的操作并且回滚当前的改变。
// 开启事务
BeforeSave
BeforeCreate
// 连表前的保存// 更新时间戳 `CreatedAt`, `UpdatedAt`// 保存自己// 重载哪些有默认值和空的字段// 链表后的保存
AfterCreate
AfterSave
// 提交或回滚事务
func (u *User) BeforeSave() (err error) {
if u.IsValid() {
err = errors.New("can't save invalid data")
}
return
}
func (u *User) AfterCreate(scope *gorm.Scope) (err error) {
if u.ID == 1 {
scope.DB().Model(u).Update("role", "admin")
}
return
}
//注意,在GORM中的保存 删除操作会默认进行事务处理,所以在事务中,所有的改变都是无效的,直到它被提交为止
事务
GORM默认在事务中执行单个create update delete操作,以确保数据库数据完整性
如果想让多个create update delete当成一个原子性操作,那么就需要使用transation
事务的正常流程
//开启事务
tx := db.Begin()
// 在事务中执行一些数据库操作 (从这里开始使用 'tx',而不是 'db')
tx.Create(...)
// ...
// 发生错误回滚事务
tx.Rollback()
// 或者提交这个事务
tx.Commit()
具体例子
func CreateAnimals(db *gorm.DB) err {
// 注意在事务中要使用 tx 作为数据库句柄
tx := db.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
if tx.Error != nil {
return err
}
if err := tx.Create(&Animal{Name: "Giraffe"}).Error; err != nil {
tx.Rollback()
return err
}
if err := tx.Create(&Animal{Name: "Lion"}).Error; err != nil {
tx.Rollback()
return err
}
return tx.Commit().Error
}
连接池
// SetMaxIdleConns 设置空闲连接池中的最大连接数。
db.DB().SetMaxIdleConns(10)
// SetMaxOpenConns 设置数据库连接最大打开数。
db.DB().SetMaxOpenConns(100)
// SetConnMaxLifetime 设置可重用连接的最长时间
db.DB().SetConnMaxLifetime(time.Hour)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!