青训营笔记6之GORM框架的认识
一、Gorm的基本使用:
GORM 是面向 Golang 语言的一种 ORM(持久层)框架,支持多种数据库的接入支持。例如MySQL、SQLServer、PostgreSQL、SQLite。此框架的特点,弱化了开发者对于 SQL 语言的掌握程度,使用提供的 API 进行底层数据库的访问。
1.1、代码示例
以下是以连接mysql数据库为例
type Product struct { //定义gorm model
Code string
Price uint
} //给model定义表名
func (p Product) TableName() string {
return "product"
}
func main() {
//连接数据库
db,err := gorm.Open(
mysql.Open("user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"),
&gorm.Config{})
if err !=nil {
panic("failed to connect database")
}
//创建数据
db.Create(&Product{Code:"D42",Price:100})
//查询数据
var product Product
db.Frist(&product,1)//根据整形主键寻找
db.Frist(&product,"code = ?","D42")//查找code字段值为D42的记录
//更新数据,将product的price更新为200
db.Model(&product).Updates("Price",200)
//更新多个字段
db.Model(&product).Updates(Product{Price:200,Code:"F42"})
db.Model(&product).Updates(map[string]interface{}{"Price":200,"Code":"F42"})
//删除数据
db.Delete(&product,1)
}
注:什么是DSN?
Data Source Name 数据源名称。DSN为ODBC定义了一个确定的数据库和必须用到的ODBC驱动程序。每个ODBC驱动程序定义为该驱动程序支持的一个数据库创建DSN需要的信息。就是说安装ODBC驱动程序以及创建一个数据库之后,必须创建一个DSN。
二、GORM创建数据库
1、如何使用Upsert?
使用clause.OnConflict处理数据冲突
p:=&Product{Code:"D42",ID: 1}
db.Clauses(clause.OnConflict{DoNothing:true}).Create(&p)
2、如何使用默认值?
通过使用default标签为字段定义默认值
type User struct {
ID int64
Name string `gorm:"default:galeone"`
Age int64 `gorm:"default:18"`
}
三、GORM查询数据库
First的使用踩坑:
- 使用First时,需要注意查询不到数据时会返回ErrRecordNotFound.
- 使用Find查询多条数据,查询不到数据不会返回错误。 使用结构体作为查询条件时: 当使用结构作为条件查询时,GORM只会查询非零值字段。这意味着如果您的字段值为0、"、false或其他零值,该字段不会被用于构建查询条件,使用Map来构建查询条件
四、GORM更新数据库
使用Struct更新时,只会更新非零值,如果需要更新零值,可以使用Map更新或使用Select选择字段
//条件更新单个列
//UPDATE users SET name ='hello' ,updated_at='2013-11-17 21:34:10'WHERE age >18
db.Model(&User{ID:111}).Where("age > ?",18).Update("name","hello")
//更新多个列
//根据`struct`更新属性,只会更新非零值的字段
//UPDATE users SET name='hello',age=18,updated_at = '2013-11-17 21:34:10'WHERE id =111
db.Model(&User{ID:111}).Updates(User{Name:"hello", age:18})
//根据`map`更新属性
//UPDATE users SET name='hello',age=18,actived=false,updated_at = '2013-11-17 21:34:10'WHERE id =111
db.Model(&User{ID:111}).Updates(map[string]interface{}{"name":"hello","age":18,"actived":false})
//更新选定字段
//UPDATE users SET name ='hello' WHERE id =111;
db.Model(&User{ID:111}).Select("name").Updates(map[string]interface{}{"name":"hello","age":18,"actived":false})
//SQL表达式更新
//UPDATE "products" SET "price" = price * 2 + 100,"updated_at"='2013-11-17 21:34:10'WHERE "id" =3;
db.Model(&User{ID:111}).Updates("age",gorm.Expr("age * ? + ?", 2,100))
五、GORM删除数据库
- 物理删除
db.Delete(&User{},10)//删除id为10的用户
db.Delete(&User{},[]int{1,2,3})//删除id为1,2,3的用户
db.Where("name LIKE ?","%jinzhu%").Delete(User{})//删除名字含有jinzhu的用户
db.Delete(User{},"email LIKE ?","%jinzhu%")//上同
- 软删除:
- GORM提供了gorm.DeleteAt用于用户实现软删
- 拥有软删除能力的Model调用Delete时,记录不会被从数据库中真正删除。但GORM会将DeleteAt置为当前时间,并且不能再通过正常的查询方法找到该记录。
- 使用Unscoped可以查询到被软删的数据
六、GORM 事务
- Gorm提供了Begin,Commit,Rollback方法用于使用事务
db,err := gorm.Open(
mysql.Open("user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"),
&gorm.Config{})
if err !=nil {
panic("failed to connect database")
}
tx := db.Begin() //开始事务
//在事务中执行一些db操作
if err = tx.Create(&User{Name:"name"}).Error; err != nil {
tx.Rollback()//遇到错误时回滚事务
return
}
if err =tx.Create(&User{Name:"name1"}).Error; err != nil {
tx.Rollback()
return
}
//提交事务
tx.Commit()
- Gorm提供了Tansaction方法用于自动提交事务,避免用户漏写Commit,Rollback。
db,err := gorm.Open(
mysql.Open("user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"),
&gorm.Config{})
if err !=nil {
panic("failed to connect database")
}
if err = db.Transaction(func(tx *gorm.DB) error {
if err tx.Create(&User{Name:"name"}).Error; err!= nil {
return err
}
if err =tx.Create(&User{Name: "name1"}).Error; err!=nil {
tx.Rollback()
return err
}
return nil
});err!=nil {
return
}
七、GORM Hook
GORM提供了CURD的Hook能力,Hook是在创建、查询、更新、删除等操作之前、之后自动调用的函数。如果任何Hook返回错误,GORM将停止后续的操作并回滚事务。
type User struct {
ID int64
Name string `gorm:"default:galeone"`
Age int64 `gorm:"default:18"`
}
type Email struct {
ID int64
Name string
Email string
}
func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
if u.Age < 0 {
return errors.New("can't save invaild data")
}
return
}
func (u *User) AfterCreate(tx *gorm.Db) (erer error) {
return tx.Create(&Email{ID: u.ID, Email: u.Name + "@***.com"}).Error
}
八、GORM性能提高
-
对于写操作(创建、更新、删除),为了确保数据的完整性,GORM会将它们封装在事务内运行。但这会降低性能,你可以使用 SkipDefaultTransation关闭默认事务。
-
使用PrepareStmt缓存预编译语句可以提高后续调用的速度,本机测试提高大约三成
db,err := gorm.Open(
mysql.Open("user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"),
&gorm.Config{
SkipDefaultTransation:true,//关闭默认事务
PrepareStmt: true},//缓存预编译语句
)
if err !=nil {
panic("failed to connect database")
}