GORM小结

# 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小结

 

 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)

 

 

上一篇:在kratos微服务中引入GORM框架


下一篇:oracle 19c sec_case_sensitive_logon=false正确密码无法登录