前言
所有的后端应用都离不开数据库的操作,在Go中也有一些好用的数据库操作组件,例如Gorm就是一个很不错的选择。
这里是Gorm自己例举的优点:
当然,你可能用不到gorm
这么多特性,但是也不阻碍Gorm
是Go
中一个非常优秀的ORM
框架。
本文也不探究Gorm
和其他框架的优劣比较,而是从使用者出发,一起来探讨Gorm
在实际开发中的使用。
当然Gorm
本身的官方文档已经非常详细了,如果对本文中的部分Gorm
使用有稍许疑惑的话,请移步官方文档:https://gorm.io/zh_CN/docs/index.html
在控制台执行go get
命令进行安装依赖,驱动根据自己的实际使用进行安装,这里以MySQL
为例。
Gorm
官方支持的数据库类型有:MySQL, PostgreSQL, SQlite, SQL Server
go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql
在使用时引入依赖即可
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
使用Gorm
建立数据库的连接其实很简单,但是要做到好用,那就需要花点心思,在这里,将带领大家怎么从最简单的连接到好用的连接设置。
func GetDb() *gorm.DB {
// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err!= nil {
return nil
}
return db
}
“注意:
更多参数设置:https://github.com/go-sql-driver/mysql#parameters ”
Gorm
同样支持连接池,Gorm
使用 database/sql
维护连接池
分别使用SetMaxIdleConns
,SetMaxOpenConns
,SetConnMaxLifetime
来设置最大空闲连接数、最大连接数和设置连接空闲超时参数。
func GetDb() *gorm.DB {
// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
})
if err != nil {
return nil
}
sqlDB, err := db.DB()
if err != nil {
log.Printf("database setup error %v", err)
}
sqlDB.SetMaxIdleConns(10) //最大空闲连接数
sqlDB.SetMaxOpenConns(100) //最大连接数
sqlDB.SetConnMaxLifetime(time.Hour) //设置连接空闲超时
return db
}
为了方便使用,我们可以在一开始就使用一个全局变量来保存数据库的连接,在使用时直接调用即可,而不需要再次进行数据库的初始化。
var db *gorm.DB
// GetDb 获取连接
func GetDb() *gorm.DB {
return db
}
将之前的函数改为给db进行初始化并赋值,在使用的时候直接调用GetDb
函数即可
func DbInit(){
// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
tempDb, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
})
if err != nil {
return nil
}
sqlDB, err := tempDb.DB()
if err != nil {
log.Printf("database setup error %v", err)
}
sqlDB.SetMaxIdleConns(10) //最大空闲连接数
sqlDB.SetMaxOpenConns(100) //最大连接数
sqlDB.SetConnMaxLifetime(time.Hour) //设置连接空闲超时
db = tempDb
}
到这里,你其实发现已经能够很好的使用Gorm
去建立数据库连接了,但是有没有什么办法像Spring Boot
一样从配置文件中获取连接参数呢,恰好第三章中讲到了怎么使用读取配置文件的方法,那何不利用起来呢?
戳 -> Go(三)Go配置文件
在配置文件中定义数据库连接参数
database:
type: mysql
host: localhost
port: 3306
username: root
password: 123456
dbname: test
max_idle_conn: 10
max_open_conn: 30
conn_max_lifetime: 300
定义相应的结构体
var Database *database
type conf struct {
DB database `yaml:"database"`
}
type database struct {
Type string `yaml:"type"`
Host string `yaml:"host"`
Port string `yaml:"port"`
UserName string `yaml:"username"`
Password string `yaml:"password"`
DbName string `yaml:"dbname"`
MaxIdleConn int `yaml:"max_idle_conn"`
MaxOpenConn int `yaml:"max_open_conn"`
ConnMaxLifetime int `yaml:"conn_max_lifetime"`
}
具体怎么绑定参数,请戳 -> Go(三)Go配置文件
为了更直观的感受,将URI
抽取出来
//获取链接URIfunc mySQLUri() string { return fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8&parseTime=true", Database.UserName, Database.Password, Database.Host, Database.Port, Database.DbName)}
那么最终呈现的就是这样。
var db *gorm.DB// GetDb 获取连接func GetDb() *gorm.DB { return db}// DbInit 数据库连接池初始化func DbInit() { fmt.Println(mySQLUri()) conn, err1 := gorm.Open(mysql.Open(mySQLUri()), &gorm.Config{}) if err1 != nil { log.Printf("connect get failed.") return } sqlDB, err := conn.DB() if err != nil { log.Printf("database setup error %v", err) } sqlDB.SetMaxIdleConns(Database.MaxIdleConn) //最大空闲连接数 sqlDB.SetMaxOpenConns(Database.MaxOpenConn) //最大连接数 sqlDB.SetConnMaxLifetime(time.Duration(Database.ConnMaxLifetime) * time.Second) //设置连接空闲超时 db = conn}
如果想要在项目启动时自动初始化,将DbInit
方法名改为init
即可,否则,需要在main
方法中自行调用执行初始化。
为了更好的开发,我们可以自定义Gorm
的日志
//初始化数据库日志newLogger := logger.New( log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer logger.Config{ SlowThreshold: time.Second, // Slow SQL threshold LogLevel: logger.Info, // Log level IgnoreRecordNotFoundError: true, // Ignore ErrRecordNotFound error for logger Colorful: true, // Disable color },)
将其作为参数放置在Gorm
参数上gorm.Open(mysql.Open(mySQLUri()), &gorm.Config{})
conn, err1 := gorm.Open(mysql.Open(mySQLUri()), &gorm.Config{ Logger: newLogger,})
Gorm
的CURD
相对来说也比较简单。
定义一个结构体User
,除开记录的字段,有编号、姓名、密码三个字段
type User struct { Id int64 `gorm:"primaryKey;column:id;"` Username string `gorm:"column:user_name;type:varchar(255);default:(-)" ` Password string `gorm:"column:password;type:varchar(255);default:(-)"` Deleted gorm.DeletedAt `gorm:"column:deleted;type:timestamp;default:(-)"` CreateTime time.Time `gorm:"column:create_time;type:timestamp;default:(-)"` UpdateTime time.Time `gorm:"column:update_time;type:timestamp;default:(-)"`}// TableName 自定义表名func (*User) TableName() string { return "users"}
说明:
primaryKey
指定主键column:id
指定在数据库中的列名gorm.DeletedAt
标明该字段为删除标志,如果使用了gorm.DeletedAt
,数据库列类型必须为时间格式。type:varchar(255)
标明字段类型default:(-)
设置默认值,-
表示为无默认值。User.TableName
表名数据库名,当使用Model
绑定结构体时,Gorm
会默认调用该方法,除此之外,还可以使用db.Table("user")
显式的标明表名。// GetFirst SELECT * FROM users ORDER BY id LIMIT 1;func GetFirst() (user *User) { db := config.GetDb() db.Model(&user).First(&user) return }
// GetLast SELECT * FROM users ORDER BY id DESC LIMIT 1;func GetLast() (user *User) { db := config.GetDb() db.Model(&user).Last(&user) return}
// GetById SELECT * FROM users WHERE id = 1;func GetById(id int64) (user *User) { db := config.GetDb() db.Model(&user).Find(&user,id) return}等同于func GetById(id int64) (user *User) { db := config.GetDb() db.Model(&user).Where("id = ?",id).Find(&user) return}
// GetByIds SELECT * FROM users WHERE id IN (1,2,3);func GetByIds(ids []int64) (user []*User) { db := config.GetDb() db.Model(&user).Find(&user,ids) return}等同于func GetByIds(s []int64) (user []*User) { db := config.GetDb() db.Model(&user).Where("id in ?",ids).Find(&user) return}
// GetSomeParam SELECT username,password FROM users WHERE id = 1;func GetSomeParam(id int64) (user *User) { db := config.GetDb() db.Model(&user).Select("username", "password").Find(&user,id) return}
Limit & Offset
进行分页查询// GetPage SELECT * FROM users OFFSET 5 LIMIT 10;func GetPage(limit int,offset int) (user []*User) { db := config.GetDb() db.Model(&user).Limit(limit).Offset(offset).Find(&user) return}
// GetByOrder SELECT * FROM users ORDER BY id desc, username;func GetByOrder() (user []*User) { db := config.GetDb() db.Model(&user).Order("id desc,username").Find(&user) return}等同于func GetByOrder() (user []*User) { db := config.GetDb() db.Model(&user).Order("id desc").Order("username").Find(&user) return}
更多请移步:https://gorm.io/zh_CN/docs/query.html
func Create(user *User) { db := config.GetDb() db.Model(&user).Create(&user) return}
func Save(user *User) { db := config.GetDb() db.Model(&user).Save(&user) return}
Create
和Save
的区别:Save
需要插入的数据存在则不进行插入,Create
无论什么情况都执行插入
func CreateBatch(user []*User) { db := config.GetDb() db.Model(&user).Create(&user) return}
更多请移步:https://gorm.io/zh_CN/docs/create.html
// UpdateUsername UPDATE users SET username = "lomtom" where id = 1func UpdateUsername(id int64,username string) { db := config.GetDb() db.Model(&User{}).Where("id = ?",id).Update("username",username) return}
// UpdateByUser UPDATE `user` SET `id`=14,`user_name`='lomtom',`password`='123456',`create_time`='2021-09-26 14:22:21.271',`update_time`='2021-09-26 14:22:21.271' WHERE id = 14 AND `user`.`deleted` IS NULLfunc UpdateByUser(user *User) { db := config.GetDb() db.Model(&User{}).Where("id = ?",user.Id).Updates(&user) return}
更多请移步:https://gorm.io/zh_CN/docs/update.html
user
里的id进行删除)// DeleteByUser DELETE from users where id = 28;// DeleteByUser UPDATE `user` SET `deleted`='2021-09-26 14:25:33.368' WHERE `user`.`id` = 28 AND `user`.`deleted` IS NULLfunc DeleteByUser(user *User) { db := config.GetDb() db.Model(&User{}).Delete(&user) return}
说明:结构体未加gorm.DeletedAt
标记的字段,直接删除,加了将更新deleted
字段,即实现软删除
id
进行删除// DeleteById UPDATE `user` SET `deleted`='2021-09-26 14:29:55.15' WHERE `user`.`id` = 28 AND `user`.`deleted` IS NULLfunc DeleteById(id int64) { db := config.GetDb() db.Model(&User{}).Delete(&User{},id) return}
同样,Gorm
也有丰富的事务支持。
可使用db.Transaction
匿名方法来表明多个操作在一个事务里面,返回err
将回滚,返回nil
将提交事务
func Transaction() error { db := config.GetDb() err := db.Transaction(func(tx *gorm.DB) error { // 在事务中执行一些 db 操作(从这里开始,您应该使用 'tx' 而不是 'db') if err := tx.Create(&User{Username: "lomtom"}).Error; err != nil { // 返回任何错误都会回滚事务 return err } if err := tx.Delete(&User{}, 28).Error; err != nil { return err } // 返回 nil 提交事务 return nil }) if err != nil { return err } return nil}
以db.Begin()
表明一个事务的开始,出现错误使用tx.Rollback()
,事务提交使用tx.Commit()
func Transaction1() error { db := config.GetDb() tx := db.Begin() defer func() { if r := recover(); r != nil { tx.Rollback() } }() // 在事务中执行一些 db 操作(从这里开始,您应该使用 'tx' 而不是 'db') if err := tx.Create(&User{Username: "lomtom"}).Error; err != nil { // 回滚事务 tx.Rollback() return err } if err := tx.Delete(&User{}, 28).Error; err != nil { tx.Rollback() return err } // 提交事务 return tx.Commit().Error}