Skip to main content

数据库基本操作

github.com/gogf/gf/v2/frame/g"包里的Model函数返回一个gdb.Model对象,提供了一系列对数据库的操作。Model函数接收一个参数,为数据表名:

md := g.Model("book")

返回一个与表book关联的Model

查询数据

One/All/Count/Value/Array/Fields

查询数据库中一条数据

md := g.Model("book")
bk, err := md.One()
if err == nil {
req.Response.WriteJson(bk)
}

返回数据库中第一条数据。查询成功返回的数据为map[string]*gvar.Var类型,所以可以直接访问里面的每一字段:

req.Response.WriteJson(bk["name"])  // 返回结果中"name"字段

可以用gvar.Var的方法对字符进行类型转换,转为需要的类型

bk["name"].String() // 转为string类型
bk["price"].Float32() // 转为float32类型

指定查询字段

bk, err := md.Fields("name, price").One() //只查询name price两个字符
// 也可以写为
bk, err := md.Fields("name", "price").One()

查询多条数据

md := g.Model("book")
bk, err := md.All()

该方法以切片返回数据表中所有数据,可以进行循环操作每一条数据

for _, v := range bk {
req.Response.Writeln(v)
}

查询数据数量

md := g.Model("book")
count, err := md.Count()

查询一条数据指定字段

md := g.Model("book")
name, err := md.Value("name")

查询指定列数据

md := g.Model("book")
name, err := md.Array("name")

Max/Min/Sum/Avg

GoFrame提供了最大最小值、求和、平均等方法

md := g.Model("book")

max, err := md.Max("price")
min, err := md.Min("price")
sum, err := md.Sum("price")
avg, err := md.Avg("price")

Where/Where*/WhereOr/WhereOr*

查询数据时可以通过Where方法指定条件,如果有多个Where,则多个条件之间会用AND连接

等于

默认情况下条件会用=连接

md := g.Model("book")
books, err := md.Where("id", 1).All()

不等

如果是不等关系,需要在字段后面加上不等符号

md := g.Model("book")
books, err := md.Where("id>", 1).All()

多个条件叠加

有多个条件时可以多个Where进行链式调用,条件会用AND连接。

md := g.Model("book")
books, err := md.Where("id>=?", 2).Where("id<?", 4).All()

Where系列方法

方法生成的SQL条件表达式
WhereLT(column, value)column < value
WhereLTE(column, value)column <= value
WhereGT(column, value)column > value
WhereGTE(column, value)column >= value
WhereBetween(column, min, max)column BETWEEN min AND max
WhereNotBetween(column, min, max)column NOT BETWEEN min AND max
WhereLike(column, like)column LIKE like
WhereIn(column, in)column IN (in)
WhereNotIn(column, in)column NOT IN (in)
WhereNot(column, value)column != value
WhereNull(columns1, columns2... )columns1 IS NULL AND columns2 IS NULL...
WhereNotNull(columns1, columns2... )columns1 IS NOT NULL AND columns2 IS NOT NULL ...

使用示例:

md := g.Model("book")
books, err := md.WhereIn("id", g.Array{1, 2, 3}).WhereLike("name", "%数据%").All()

// 生成如下SQL
// SELECT * FROM `book` WHERE (`id` IN (1,2,3)) AND (`name` LIKE '%数据%')

以上方法如果链式调用会生成以AND连接的条件,如果需要生成以OR连接的条件,则需要用到下列方法:

WhereOr系列方法

方法生成的SQL条件表达式
WhereOrLT(column, value)OR (column < value)
WhereOrLTE(column, value)OR (column <= value)
WhereOrGT(column, value)OR (column > value)
WhereOrGTE(column, value)OR (column >= value)
WhereOrBetween(column, min, max)OR (column BETWEEN min AND max)
WhereOrNotBetween(column, min, max)OR (column NOT BETWEEN min AND max)
WhereOrLike(column, like)OR (column LIKE like)
WhereOrIn(column, in)OR (column IN (in))
WhereOrNotIn(column, in)OR (column NOT IN (in))
WhereOrNot(column, value)OR (column != value)
WhereOrNull(columns1, columns2... )OR (columns1 IS NULL AND columns2 IS NULL...)
WhereOrNotNull(columns1, columns2... )OR (columns1 IS NOT NULL AND columns2 IS NOT NULL ...)
WhereOr(column, value)OR (column = value)

示例:

md := g.Model("book")
books, err := md.WhereIn("id", g.Array{1, 2, 3}).WhereOrLike("name", "%数据%").All()
// 生成如下SQL
// SELECT * FROM `book` WHERE (`id` IN (1,2,3)) OR (`name` LIKE '%数据%')

Group/Order/Order*

按字段分组

md := g.Model("book")
books, err := md.Group("name").All()

按字段排序

md := g.Model("book")
books, err := md.Order("price", "DESC").All()
// 多字段排序
books, err := md.Order("price", "DESC").Order("id", "ASC").All()
// 排序封装方法
books, err := md.OrderDesc("price").OrderAsc("id").All()

Scan

OneAll返回的数据为Map或者Map切片,在实际使用当中查询到的数据可能需要转换为特定的数据结构方便使用。

Scan方法可以将查询到的数据转为自定义结构体或结构体数组。该方法使用方式非常灵活,示例中只演示推荐写法。

查询数据转为自定义结构体

type Book struct {
Id uint
Name string
Author string
Price float64
PublishTime *gtime.Time
}

var book *Book

md := g.Model("book")
err := md.Scan(&book)

Scan会将数据库字段下划线命名对应到结构体中相应的驼峰命名上,如果对应不上,则该成员为nil或者零值。如果结构体中成员名称与数据表中字段不对应,可以用orm:标签来指定对应字段

type Book struct {
BookId uint `orm:"id" `
BookName string `orm:"name"`
BookAuthor string `orm:"author"`
BookPrice float64 `orm:"price"`
PubTime *gtime.Time `orm:"publish_time"`
}

var book *Book

md := g.Model("book")
err := md.Scan(&book)

结构体数组

Scan方法可以查询单独结构体,如上,也可以查询一个结构体数组,只需要将结构体指针改为结构体切片传入即可

type Book struct {
Id uint
Name string
Author string
Price float64
PublishTime *gtime.Time
}

var book []Book

md := g.Model("book")
err := md.Scan(&book)

查询结果为一个由Book组成的结构体数组,存放多条数据。

查询部分暂时就先了解这些,实际上只要SQL熟悉的话每种查询基本上都能找到对应的方法来实现。更复杂的查询见官方文档ORM查询

查询结果为空判断

All

md := g.Model("book")
books, _ := md.All()
if len(books) == 0 {
g.RequestFromCtx(ctx).Response.Writeln("结果为空")
}
// 或者
if books.IsEmpty() {
g.RequestFromCtx(ctx).Response.Writeln("结果为空")
}

One

md := g.Model("book")
book, _ := md.Where("id", 100).One()
if len(book) == 0 {
g.RequestFromCtx(ctx).Response.Writeln("结果为空")
}
// 或者
if book.IsEmpty() {
g.RequestFromCtx(ctx).Response.Writeln("结果为空")
}

Value

md := g.Model("book")
name, _ := md.Where("id", 10).Value("name")
if name.IsEmpty() {
g.RequestFromCtx(ctx).Response.Writeln("结果为空")
}

Array

md := g.Model("book")
names, _ := md.WhereLT("id", 10).Array("name")
if len(names) == 0{
g.RequestFromCtx(ctx).Response.Writeln("结果为空")
}

Scan结构体对象

var book *Book
md := g.Model("book")
md.Scan(&book)
if book == nil {
g.RequestFromCtx(ctx).Response.Writeln("结果为空")
}
md.Save(data)

Scan结构体数组

var books []Book
md := g.Model("book")
md.Scan(&books)
if len(books) == 0 {
g.RequestFromCtx(ctx).Response.Writeln("结果为空")
}

分页

GoFrame中提供了Page方法可以很方便实现分页查询,只需提供页数和每页数据数量即可。

md := g.Model("book")
books, err := md.Page(1, 5).All()

也有Limit方法可以用来限制查询条数以及自定义起始位置与数据限制

md := g.Model("book")
// 限制条数
books, err := md.Limit(5).All()
// 指定起始位置与限制条数
books, err := md.Limit(3, 5).All()

插入数据

Insert/Replace/Save

这三个方法都可以向数据库中写入一条或者多条数据,区别在于当数据中主键字段在数据库中已经存在时,处理方式不同:

方法主键在数据库中已存在时
Insert报错,主键冲突
Repalce用提供的数据替换已存在同主键的数据
Save用提供的数据更新已存在的同主键数据

写入单条数据

md := g.Model("book")
data := g.Map{
"id": 8,
"name": "Linux驱动开发入门与实践",
"author": "郑强",
"price": 69,
"publish_time": "2023-10-10",
}
// Insert
result, err := md.Insert(data)
// Replace
result, err := md.Replace(data)
// Save
result, err := md.Save(data)

以上方法也可配合Data使用

// Insert
result, err := md.Data(data).Insert()
// Replace
result, err := md.Data(data).Replace()
// Save
result, err := md.Data(data).Save()

除了使用Map类型之外,还可以用结构体。结构体成员名称与数据表字段名称不对应时,用orm标签指定

type Book struct {
Id uint
Name string
Author string
Price float64
PubTime *gtime.Time `orm:"publish_time"`
}


md := g.Model("book")
data := Book{
Id: 8,
Name: "Linux驱动开发入门与实践",
Author: "郑强",
Price: 69.3,
PubTime: gtime.New("2023-10-10"),
}
result, err := md.Data(data).Save()

批量写入数据

上述方法也可以批量写入数据

data := g.List{
g.Map{
"name": "Linux驱动开发入门与实践",
"author": "郑强",
"price": 69.3,
"publish_time": gtime.New("2023-10-10"),
},
g.Map{
"name": "Linux驱动开发入门与实践",
"author": "郑强",
"price": 69.3,
"publish_time": gtime.New("2023-10-10"),
},
g.Map{
"name": "Linux驱动开发入门与实践",
"author": "郑强",
"price": 69.3,
"publish_time": gtime.New("2023-10-10"),
},
}

result, err := md.Data(data).Save()

如果使用的是结构体,将g.List改为g.Array或者g.Slice

InsertAndGetId

写入数据并返回自增ID

data := g.Map{
"name": "Linux驱动开发入门与实践",
"author": "郑强",
"price": 69.3,
"publish_time": gtime.New("2023-10-10"),
}

result, err := md.Data(data).InsertAndGetId()

gdb.Raw

对于有的字段,可能需要调用SQL里面的操作来获得结果,例如,publish_time字段可以用SQL中的CURRENT_DATE()来获取当前日期,这时就需要用到Raw

data := g.Map{
"name": "Linux驱动开发入门与实践",
"author": "郑强",
"price": 69.3,
"publish_time": gdb.Raw("CURRENT_DATE()"),
}

result, err := md.Data(data).InsertAndGetId()

更新数据

Update

data := g.Map{
"author": "郑强强",
"price": 69.333,
}

result, err := md.Where("author", "郑强").Update(data)

也可以配合Data使用

data := g.Map{
"author": "郑强强",
"price": 69.333,
}

result, err := md.Where("author", "郑强").Data(data).Update()

Increment/Decrement

用来给指定字段增加/减少指定值

result, err := md.WhereBetween("id", 7, 10).Increment("price", 2.5)
result, err := md.WhereBetween("id", 7, 10).Decrement("price", 1.5)

删除数据

result, err := md.WhereGT("id", 10).Delete()