目录
本文概览:类似java的mybatis,golang的mysql框架是GORM。
1 介绍
1.1 约定
GORM 倾向于约定,而不是配置。默认情况下,GORM 使用 ID 作为主键,使用结构体名的 蛇形复数 作为表名,字段名的 蛇形 作为列名,并使用 CreatedAt、UpdatedAt 字段追踪创建、更新时间。
1、表名映射。蛇形复数
实现 Tabler 接口来更改默认表名,例如:
1 2 3 4 5 6 |
type User struct { ID uint // 列名是 `id` Name string // 列名是 `name` Birthday time.Time // 列名是 `birthday` CreatedAt time.Time // 列名是 `created_at` } |
GORM 定义一个 gorm.Model 结构体,其包括段 ID、CreatedAt、UpdatedAt、DeletedAt
1 2 3 4 5 6 7 |
// gorm.Model 的定义 type Model struct { ID uint `gorm:"primaryKey"` CreatedAt time.Time UpdatedAt time.Time DeletedAt gorm.DeletedAt `gorm:"index"` } |
2 使用Demo
准备
1 2 3 4 5 6 7 8 9 10 11 |
CREATE TABLE `student` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(45) NOT NULL, `score` int(11) DEFAULT NULL, `des` varchar(45) NOT NULL DEFAULT 'hello', `create_time` timestamp NULL DEFAULT NULL, `update_time` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `unq_nam` (`name`), KEY `idx_score` (`score`,`name`) ) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8 |
1、第三方依赖
1 2 |
go get -u gorm.io/gorm go get -u gorm.io/driver/mysql |
第一个:核心库
第二个:mysql驱动
2、数据模型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
type Student struct { ID int `gorm:"column:id"` Name string `gorm:"column:name"` Score int `gorm:"column:score"` Des string `gorm:"column:des"` CreateTime time.Time `gorm:"column:create_time"` UpdateTime time.Time `gorm:"column:update_time"` } // 重新定义表名,默认格式为 蛇形复数 func (Student) TableName() string{ return "student" } |
2.1 数据库连接和数据库连接池
1. 数据库连接
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
func TestDb(){ // 1.数据库连接 db,err := gorm.Open(mysql.Open("root:53061208@tcp(127.0.0.1:3306)/test"),&gorm.Config{}) if err!= nil{ fmt.Println(err) return }else { // 初始化连接持池 initDbPool(db) fmt.Println("connection init success") } // 2.新增用户 // addTest(db) } |
问题1:如果出现”: Scan error on column index 4, name “create_time”: unsupported Scan, storing driver.Value type []uint8 into type *time.Time“
解决:添加”?parseTime=true to the db mapper. Like this:”
1 |
db, err := sqlx.Connect("mysql", "myuser:mypass@tcp(127.0.0.1:3306)/mydb?parseTime=true" |
2、数据库连接池配置
1 2 3 4 5 6 7 8 9 10 11 |
// 数据库连接池 func initDbPool(db *gorm.DB){ sqlDb,err := db.DB() if err!=nil{ panic("get db pool error") } sqlDb.SetMaxIdleConns(5) sqlDb.SetMaxOpenConns(10) // ..其他连接池配置 } |
2.2 数据连接和mysql新增
2.2.1 新增一列
1 2 3 4 5 6 7 8 9 10 11 12 |
func addTest(db *gorm.DB){ s1 := Student{ Name:"t11", Score:20, Des:"t11", CreateTime:time.Now(), UpdateTime:time.Now(), } // 传入需要是一个指针类型,否则 id映射会失败:panic: reflect: reflect.Value.SetInt using unaddressable value db.Create(&s1) } |
2.2.2 批量新增
使用切片进行批量插入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
// 批量插入 func addBatch(db *gorm.DB){ // 定义数组 s1 := Student{ Name:"t22", Score:20, Des:"t22", CreateTime:time.Now(), UpdateTime:time.Now(), } s2 := Student{ Name:"t23", Score:20, Des:"t23", CreateTime:time.Now(), UpdateTime:time.Now(), } list := make([]Student,2) list[0]=s1 list[1]=s2 // 2.新增数组 db.Create(list) } |
2.3 insert ignore
Clause Options
GORM defined Many Clauses, and some clauses provide advanced options can be used for your application
Although most of them are rarely used, if you find GORM public API can’t match your requirements, may be good to check them out, for example:
1 2 |
db.Clauses(clause.Insert{Modifier: "IGNORE"}).Create(&user) // INSERT IGNORE INTO users (name,age...) VALUES ("jinzhu",18...); |
2.3 查询
2.3.1 查询一个元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
func queryTestForObject(db *gorm.DB) Student { // 查询一个对象 var s Student err := db.Clauses().Table("student").Where("id = ?", 16).First(&s).Error if err != nil && err == gorm.ErrRecordNotFound { fmt.Println("record not found") } else if err != nil { fmt.Println(err) }else { fmt.Println(s) } return s } |
查询结果为
1 |
{13 t11 20 t11 0001-01-01 00:00:00 +0000 UTC 0001-01-01 00:00:00 +0000 UTC} |
Find VS First区别
- 如果使用First,差不到时候会返回gorm.ErrRecordNotFound。
- 如果是Find,则不会返回任何错误。
Find VS Scan 区别
- find返回数据是数据库Entity。
- scan可以支持映射成非Entity的struct对象。比如
1 2 3 4 5 6 |
type result struct { Date time.Time Total int } db.Model(&User{}).Select("name, sum(age) as total").Where("name LIKE ?", "group%").Group("name").First(&result) |
2.3.2 查询一个list
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
func queryTestForList(db *gorm.DB) { // 查询一个对象 var s []Student err := db.Table("student").Where("create_time>? and update_time>?", "2020-09-01 00:00:00", "2020-09-01 00:00:00").Find(&s).Error if err != nil && err == gorm.ErrRecordNotFound { fmt.Println("err:= record not found") return } else if err != nil { fmt.Println(err) return } fmt.Println("success") for i := 0; i < len(s); i++ { fmt.Println(s[i]) } } |
返回结果
1 2 |
{12 t10 20 t10 0001-01-01 00:00:00 +0000 UTC 0001-01-01 00:00:00 +0000 UTC} {13 t11 20 t11 0001-01-01 00:00:00 +0000 UTC 0001-01-01 00:00:00 +0000 UTC} |
2.4 更新
2.4.1 根据entity进行更新
获取一个entity,然后根据entity的主键进行更新。分为更新单个列和多个列两种情况
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
func updateTestForObjectWithPrimaryKey(db *gorm.DB){ su := queryTestForObject(db) // 1.更新单个列 // udpate student set name='tu' where id=xx; db.Model(&su).Update("name","tu") // 2.更新多个列 // 2.1 方式1:使用Student // udpate student set name='tu2',des='tu2' where id=xx; db.Model(&su).Updates(Student{Name:"tu2",Des: "tu2"}) // 2.2方式2:使用map // udpate student set name='tu2',des='tu2' where id=xx; dic := make(map[string]interface{}) dic["name"]="tumap" // 使用和数据库相同字段 dic["des"]="tumap" // 使用和数据库相同字段 db.Model(&su).Updates(dic) } |
2.4.2 根据自定义where条件进行更新
直接拼接where条件进行更新。分为单个列和多个列情况
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
func updateTestForObjectWithWhere(db *gorm.DB){ // 1.更新单个列 db.Model(&Student{}).Where("id = ?",13).Update("name","tuu") // 2.更新多个列 // 2.1 方式1:使用Student // udpate student set name='tu2',des='tu2' where id=xx; db.Model(&Student{}).Where("id = ?",13).Updates(Student{Name:"tu2",Des: "tu2"}) // 2.2方式2:使用map // udpate student set name='tu2',des='tu2' where id=xx; dic := make(map[string]interface{}) dic["name"]="tumap2" // 使用和数据库相同字段 dic["des"]="tumap2" // 使用和数据库相同字段 db.Model(&Student{}).Where("id = ?",13).Updates(dic) } |
3 悲观锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
func (dao *StudentDao) FindStudentForUpdate(id int,db *gorm.DB) (*Student, error) { var r Student tx := db.Begin() defer func() { if r := recover(); r != nil { tx.Rollback() } }() if err := tx.Error; err != nil { return nil, err } // 锁住指定 id 的 User 记录 if err := tx.Set("gorm:query_option", "FOR UPDATE").Where("id = ? ", id).Find(&r).Error; err != nil { tx.Rollback() return nil, err } // commit事务,释放锁 if err := tx.Commit().Error; err != nil { return nil, err } return &r,nil } |
4 事务
4.1 db.Transaction
4.1.1单事务
使用db.Transaction函数来执行事务的相关操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
func transactionTest(db *gorm.DB){ db.Transaction(func(tx *gorm.DB) error { // 1.执行sql操作 // sql1操作 tx.Create(&Student{ Name:"trans1", Score: 20, Des: "trans1", CreateTime: time.Now(), UpdateTime: time.Now(), }) // 其他操作 // 返回异常触发回滚 panic("exception") return nil }) } |
4.1.2 嵌套事务
嵌套子事务回滚,不影响其他事务。但是大事务就回滚了,大事务回滚,所有子事务都要回滚。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
// 嵌套事务验证 func nestTransactionTest(db *gorm.DB){ // 1.大事务 db.Transaction(func(tx1 *gorm.DB) error { // 1.1执行sql操作 // sql1操作 tx1.Create(&Student{ Name:"trans2", Score: 20, Des: "trans2", CreateTime: time.Now(), UpdateTime: time.Now(), }) // 2.嵌套事务1,回滚事务 tx1.Transaction(func(tx2 *gorm.DB) error { // 1.执行sql操作 // sql1操作 tx2.Create(&Student{ Name:"trans3", Score: 20, Des: "trans3", CreateTime: time.Now(), UpdateTime: time.Now(), }) // 注意:这里是返回错误,不能是通过panic跑异常,否则大事务就回滚了,大事务回滚,所有子事务都要回滚 return errors.New("nest transaction1 error") }) // 3.嵌套事务3,提交 tx1.Transaction(func(tx2 *gorm.DB) error { // 1.执行sql操作 // sql1操作 tx2.Create(&Student{ Name:"trans4", Score: 20, Des: "trans4", CreateTime: time.Now(), UpdateTime: time.Now(), }) return nil }) return nil }) } |
4.2 手动
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// 开始事务 tx := db.Begin() // 在事务中执行一些 db 操作(从这里开始,您应该使用 'tx' 而不是 'db') tx.Create(...) // ... // 遇到错误时回滚事务 tx.Rollback() // 否则,提交事务 tx.Commit() |
举例如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
func CreateAnimals(db *gorm.DB) error { // 再唠叨一下,事务一旦开始,你就应该使用 tx 处理数据 tx := db.Begin() defer func() { if r := recover(); r != nil { tx.Rollback() } }() if err := tx.Error; err != 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 } |
4.3 保存点
1 2 3 4 5 6 7 8 |
tx := DB.Begin() tx.Create(&user1) tx.SavePoint("sp1") tx.Create(&user2) tx.RollbackTo("sp1") // 回滚 user2 tx.Commit() // 最终仅提交 user1 |
参考
https://www.jianshu.com/p/9b906b62b2cc