- GORM的简单的例子(以mysql数据库为例)
- GORM的增删改查
- GORM的事务简单介绍
1.GORM的简单例子
The fantastic ORM library for Golang aims to be developer friendly.
Gorm是一个对开发者友好的ORM框架。
1.1 安装GORM
go get -u gorm.io/gorm
go get -u gorm.io/driver/sqlite
1.2 简单实现例子
type Product struct {
gorm.Model
name string
Price uint
number uint
}
func main() {
var dsn = "user:password@(localhost)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic("不能·成功连接数据库")
}
//接下来将gorm的框架注入到数据库
db.AutoMigrate(&Product{})
//接下来进行简单的创建数据,修改数据,删除数据和查询数据
db.Create(&Product{name: "加菲猫",
Price: 2200, number: 12,
}) //创建数据
//查询数据和修改数据
var product Product
db.First(&product, 1) //使用主键进行查询
db.First(&product, "name=?", "加菲猫") //查找name叫做加菲猫的商品
//加菲猫价格上涨,我们进行更新
db.Model(&product).Update("price", 2400)
//对所有信息都进行更新
db.Model(&product).Updates(Product{name: "加菲猫肥", Price: 3000, number: 8})
db.Model(&product).Updates(map[string]interface{}{"name": "加菲猫肥", "price": "3200", "number": "6"})
//删除
db.Delete(&product, 1)
}
上面实现的代码结果如下所示
上面是一个简单实现的GORM连接数据库进行简单操作的例子,下面会对其进行简单的解释。
GORM中存在一些约定,比如默认使用ID作为主键,使用结构体名的
蛇形复数 作为表名,字段名的 蛇形 作为列名,并使用 CreatedAt、UpdatedAt 字段追踪创建、更新时间,DeleteAt代表软删除。例如我们使用Product作为结构体,最终数据库生成的是products。蛇形的含义是单词之间的分隔符是_,例如UpdateTime的蛇型风格就是update_time。
上面定义了DSN,其各个字段的含义如下:username代表数据库账号,password代表密码,host代表链接地址,port代表链接端口,Dbname代表数据库名称。
上面的一段代码的GORM的增删改查方法如下:使用Create创建一个数据,Update更新数据的一项,Updates更新全部数据,First进行查询,Delete进行删除。
此外GORM存储一个默认的结构体GORM.Model,我们日常的时候可以直接拿来用。
// gorm.Model 的定义
type Model struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
}
例如我们直接将GORM.Model注入到Product中。此外对于正常的结构体,我们也可以使用embedded 将其嵌入到结构体。例如
type Product struct {
gorm.Model
name string
Price uint
number uint
}
type User struct{
gorm.Model
product Product embedded
}
1.3遇到的问题
我在连接本地mysql数据考的时候出现以下错误,failed to initialize database, got error Error 1049 (42000): Unknown database 'gorm'。
我这里是数据库没有建立,因为没有设置自动建库,所以需要我们手动提前建立好数据库,或者设置一下自动建立数据库。
2.GORM的增删改查
2.1 GORM的插入
插入数据
user := User{
Username:"xxy",
Password:"125245",
}
db.Create(&user)
返回内容主要是 user.ID // 返回插入数据的主键 result.Error // 返回 error result.RowsAffected // 返回插入记录的条数 并且我们使用Create创建数据的时候可以创建多个数据,而非只是一个数据。
此外还可以使用指定的字段创建数据
db.Select("Name", "Age", "CreatedAt").Create(&user)
我们有时候大量数据需要创建,可以进行批量创建数据。
var users = []User{{Username: "jinzhu1"}, {Username: "jinzhu2"}, {Username: "jinzhu3"}}
db.Create(&users)
for _, user := range users {
user.ID // 1,2,3
}
还可以指定创建的数目,例如指定创建数量为100,db.CreateInBatches(users, 100) 不过需要前提确实有这些数据。
Gorm还支持根据Map进行创建和根据SQL语句进行创建,其代码如下所示
db.Model(&User{}).Create([]map[string]interface{}{
{"Name": "jinzhu_1", "Age": 18},
{"Name": "jinzhu_2", "Age": 20},
})
批量插入数据从 []map[string]interface{}{} 中进行。 下面使用原生SQL进行插入数据
db.Exec("insert into users (username,password,createtime) values (?,?,?)", user.Username, user.Password, user.CreateTime)
2.2 GORM的更新
Save 会保存所有的字段,即使字段是零值,其中save是一个组合函数。如果 save value 不包含主键,它将执行 Create,否则将执行 Update (包含所有字段)。
db.Save(&User{Name: "jinzhu", Age: 100})
如果user以及有对象就更新,没有对象就创建相关值。
更新单列
db.Model(&User{}).Where("active = ?", true).Update("name", "hello")
当更新单列的时候,需要有条件语句where否则他不知道更新哪里。
更新多列
db.Model(&user).Updates(User{Name: "hello", Age: 18, Active: false})
db.Model(&user).Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
Updates支持使用struct或map[string]接口{}进行更新,当使用struct进行更新时,默认情况下只更新非零字段,这一点需要注意很多情况下很多人会出现问题。
更新选定字段
db.Model(&user).Select("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
上面的结果等价于UPDATE users SET name='hello' WHERE id=111; 所有没有被更新的数据相等于被默认了Omit,他和下面这句sql相互补充
db.Model(&user).Omit("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
下面这句代码等价于UPDATE users SET age=18, active=false, updated_at='2013-11-17 21:34:10' WHERE id=111;
批量更新
db.Model(User{}).Where("role = ?", "admin").Updates(User{Name: "hello", Age: 18})
这是批量更新结构体,更新user对象的name和age进行更新。
2.3 GORM的删除
根据主键删除
DB.Delete(&Goods{}, 1)
2.4 GORM的查询
查询数据可以分为查询一条数据,主键正序查询,主键倒序查询,查询多条数据 其代码展示如下所示:
db.Take(&user)
db.First(&user)
db.Last(&user)
db.Find(&user)
其代表的意思分别如下: Take获取一条记录,没有指定排序字段 First获取第一条记录(主键升序) Last获取最后一条记录(主键降序) Find查询多条记录
通过db.Where函数设置条件 例如
db.Where("id in (?)", []int{1,2,5,6}).Take(&goods)
还可以我们只返回几个指定的数据,而非一整个对象
DB.Select("id", "title").Find(&goods)
我们还可以排序
DB.Order("id desc").Find(&goods)
通过Order字段对于查询的数据进行排序
分页
DB.Order("create_time desc").Limit(10).Offset(10).Find(&goods)
通过limit和offset对于数据进行分页操作。
分组
type Result struct {
Type int
Total int
}
var results []Result
db.Model(Goods{}).Select("type, count(*) as total").Group("type").Having("total > 0").Scan(&results)
上面的这个代码通过分组将数据分成了两组,一组数据total>0,另一组total小于0。这里的scan类似Find都是用于执行查询语句,然后把查询结果赋值给结构体变量,区别在于scan不会从传递进来的结构体变量提取表名.
直接执行SQL语句
sql := "SELECT type, count(*) as total FROM `goods` where create_time > ? GROUP BY type HAVING (total > 0)"
db.Raw(sql, "2022-11-06 00:00:00").Scan(&results)
fmt.Println(results)
首先因为sql语句使用了一个问号(?)作为绑定参数, 所以需要传递一个绑定参数(Raw第二个参数),并且Raw函数支持绑定多个参数。