[Go三件套(1)|青训营笔记]

87 阅读7分钟

这是我参与「第五届青训营」伴学笔记创作活动的第6天

一、三件套

Gorm 是一个已经迭代了10年+的功能强大的OR

M框架,在字节内部被广泛使用并且拥有非常丰富的开源扩展

Kitex 是字节内的Golang微服务RPC框架,具有高性能、强可扩展的主要特点,支持多协议并且拥有丰富的开源扩展

Hertz 字节内部的HTTP框架,参考了其他开源框架的优势,结合字节跳动内部的需求,具有高易用性,高性能、高扩展性特点

三件套的使用

Gorm的基础使用

type Product struct {
  code string
  Price unic
}//定义gorm model

func (p Product) TableName() string{
  return "product"
}//为model定义表名

func main(){
  db,err:=gorm.Open(
  mysql.Open("user:pass@tcp(127.0.0.1:3306)/dbname?   charset=utf8mb4&parseTime=True&loc+Local),
  &grom.Config{})//链接数据库,此处可加判断错误信息

if err!=nil{
  panic("failed to connect database")
}//创建数据
//Creat
db.Creat(&Product{code:"D42",Price:100})
//Read   查询数据
var product Product
db.First(&product,1)//根据整形主键查找
db.First(&product,"code=?","D42")//查找code字段值为D42的记录
//Update — 将product的price更新为200,更新数据
db.Model(&product).Update("Price",200)
//Update — 更新多个字段
db.Model(&product).Updates(Product{Price:200,Code:"F42"})
//仅更新非零值字段
db.Model(&product).Updates(map[string]interface{}{"Price":200,"Code":"F42"})
//Delete — 删除product,删除数据
db.Delete(&Product,1)

Gorm 的约定(默认)

Gorm使用名为ID的字段作为主键

使用结构体的蛇形负数作为表名

字段名的蛇形作为列名

使用CreatedAt、UpdatedAt字段作为创建、更新时间

Gorm支持的数据库

GORM目前支持 MySQL.SQLServer。PostgreSQL、SQLite

以连接SQLServer数据库为例

import(
  "grom.io/driver/sqlserver"
  "gorm.io/gorm"
)
  //github.com/denisenkom/go-mssqlbd
dsn:="sqlserver://gorm:LoremIpsum86@localhost:9930?database=gorm"
db.err:=gorm.Open(sqlserver.Open(dsn).&gorm.Config{ })

GORM通过驱动来连接数据库,如果需要链接其它类型的数据库,可以复用/自行开发驱动

GORM创建数据

package main

import(
 “fmt”
 “gorm.io/driver/mysql"
 "gorm.io/gorm"
)
type Product struct{
  ID uint  'gorm:"prinmarykey" '
  Code string  'gorm:"column:code" '
  Price uint ' gorm:"column:user_id" '
}
func main( ){
  db,err := gorm.0pen(mysql.0pen( dsn: "usernare: pas sward@tcp(localhest:9910)    /database?charset=utf8" ),
  &gorm.Config{})
if err != nil : "failed 10 connect database"

//创建一条

p ;= &Product{code: ”D42"}

res: db.Create(p)

fmt.Println(res. Error)//获取 err

fmt.Println(p.ID)//返回插入数据的主键

//创建多条

products :[ ]*Product{{Code: "D41"},{Code:"D42"},{Code:"D43"}

res = db. Create (products)

fmt. Println(res. Error]//获取 err

for _,p ;=rarge products{

fmt.Println(p,ID)

}

如何使用Upsert

使用clause.OnConflict处理数据冲突

//以不处理冲突为例,创建一条数据

p:=&Product{Code:"D42",ID:1}

db.Clauses(clause.OnConflict{DoNothing:true}).Creat(&p)

如何使用默认值

通过使用default标签为字段定义默认值

type User struct{
  ID  int64
  Name    string  ' gorm:"default:galeone" '
  Age  int64  ' gorm:"default:18" '
}

查询

检索单个对象

GORM 提供了 First、Take、Last 方法,以便从数据库中检索单个对象。当查询数据库时它添加了 LIMIT 1

条件,且没有找到记录时,它会返回 ErrRecordNotFound 错误

// 获取第一条记录(主键升序)

db.First(&user)

// SELECT * FROM users ORDER BY id LIMIT 1;

// 获取一条记录,没有指定排序字段

db.Take(&user)

// SELECT * FROM users LIMIT 1;

// 获取最后一条记录(主键降序)

db.Last(&user)

// SELECT * FROM users ORDER BY id DESC LIMIT 1;

result := db.First(&user)

result.RowsAffected // 返回找到的记录数

result.Error // returns error

// 检查 ErrRecordNotFound 错误

errors.Is(result.Error, gorm.ErrRecordNotFound)

First、Last 方法会根据主键查找到第一个、最后一个记录, 它仅在通过 struct 或提供 model 值进行查询时才起作用。 如果 model 类型没有定义主键,则按第一个字段排序,例如:

var user User

// 可以 DB.First(&user) // SELECT * FROM users ORDER BY users.id LIMIT 1

// 可以 result := map[string]interface{}{} DB.Model(&User{}).First(&result) // SELECT * FROM users ORDER BY users.id LIMIT 1

// 不行 result := map[string]interface{}{} DB.Table("users").First(&result)

// 但可以配合 Take 使用 result := map[string]interface{}{} DB.Table("users").Take(&result)

// 根据第一个字段排序 type Language struct { Code string Name string } DB.First(&Language{}) // SELECT * FROM languages ORDER BY languages.code LIMIT 1 根据主键检索 GORM 允许通过内联条件指定主键来检索对象,但只支持整形数值,因为 string 可能导致 SQL 注入。查看 内联条件、安全 获取详情

db.First(&user, 10)

// SELECT * FROM users WHERE id = 10;

db.First(&user, "10")

// SELECT * FROM users WHERE id = 10;

db.Find(&users, []int{1,2,3})

// SELECT * FROM users WHERE id IN (1,2,3);

检索对象

// 获取全部记录

result := db.Find(&users)

// SELECT * FROM users;

result.RowsAffected // 返回找到的记录数,相当于 len(users)

result.Error // returns error

条件

String 条件

// 获取第一条匹配的记录

db.Where("name = ?", "jinzhu").First(&user)

// SELECT * FROM users WHERE name = 'jinzhu' ORDER BY id LIMIT 1;

// 获取全部匹配的记录

db.Where("name <> ?", "jinzhu").Find(&users)

// SELECT * FROM users WHERE name <> 'jinzhu';

// IN

db.Where("name IN ?", []string{"jinzhu", "jinzhu 2"}).Find(&users)

// SELECT * FROM users WHERE name IN ('jinzhu','jinzhu 2');

// LIKE

db.Where("name LIKE ?", "%jin%").Find(&users)

// SELECT * FROM users WHERE name LIKE '%jin%';

// AND

db.Where("name = ? AND age >= ?", "jinzhu", "22").Find(&users)

// SELECT * FROM users WHERE name = 'jinzhu' AND age >= 22;

Time

db.Where("updated_at > ?", lastWeek).Find(&users)

// SELECT * FROM users WHERE updated_at > '2000-01-01 00:00:00';

// BETWEEN

db.Where("created_at BETWEEN ? AND ?", lastWeek, today).Find(&users)

// SELECT * FROM users WHERE created_at BETWEEN '2000-01-01 00:00:00' AND '2000-01-08 00:00:00';

Struct & Map 条件

Struct

db.Where(&User{Name: "jinzhu", Age: 20}).First(&user)

// SELECT * FROM users WHERE name = "jinzhu" AND age = 20 ORDER BY id LIMIT 1;

Map

db.Where(map[string]interface{}{"name": "jinzhu", "age": 20}).Find(&users)

// SELECT * FROM users WHERE name = "jinzhu" AND age = 20;

主键切片条件

db.Where([]int64{20, 21, 22}).Find(&users)

// SELECT * FROM users WHERE id IN (20, 21, 22);

注意 当使用结构作为条件查询时,GORM 只会查询非零值字段。这意味着如果您的字段值为 0、''、false 或其他 零值,该字段不会被用于构建查询条件

db.Where(&User{Name: "jinzhu", Age: 0}).Find(&users)

// SELECT * FROM users WHERE name = "jinzhu";

您可以使用 map 来构建查询条件,例如:

db.Where(map[string]interface{}{"Name": "jinzhu", "Age": 0}).Find(&users)

// SELECT * FROM users WHERE name = "jinzhu" AND age = 0;

内联条件

用法与 Where 类似

// SELECT * FROM users WHERE id = 23;

// 根据主键获取记录,如果是非整型主键

db.First(&user, "id = ?", "string_primary_key")

// SELECT * FROM users WHERE id = 'string_primary_key';

// Plain SQL

db.Find(&user, "name = ?", "jinzhu")

// SELECT * FROM users WHERE name = "jinzhu";

db.Find(&users, "name <> ? AND age > ?", "jinzhu", 20)

// SELECT * FROM users WHERE name <> "jinzhu" AND age > 20;

// Struct

db.Find(&users, User{Age: 20})

// SELECT * FROM users WHERE age = 20;

// Map

db.Find(&users, map[string]interface{}{"age": 20})

// SELECT * FROM users WHERE age = 20;

Not 条件

构建 NOT 条件,用法与 Where 类似

db.Not("name = ?", "jinzhu").First(&user)

// SELECT * FROM users WHERE NOT name = "jinzhu" ORDER BY id LIMIT 1;

// Not In

db.Not(map[string]interface{}{"name": []string{"jinzhu", "jinzhu 2"}}).Find(&users)

// SELECT * FROM users WHERE name NOT IN ("jinzhu", "jinzhu 2");

// Struct

db.Not(User{Name: "jinzhu", Age: 18}).First(&user)

// SELECT * FROM users WHERE name <> "jinzhu" AND age <> 18 ORDER BY id LIMIT 1;

// 不在主键切片中的记录

db.Not([]int64{1,2,3}).First(&user)

// SELECT * FROM users WHERE id NOT IN (1,2,3) ORDER BY id LIMIT 1;

Or 条件

db.Where("role = ?", "admin").Or("role = ?", "super_admin").Find(&users)

// SELECT * FROM users WHERE role = 'admin' OR role = 'super_admin';

// Struct

db.Where("name = 'jinzhu'").Or(User{Name: "jinzhu 2", Age: 18}).Find(&users)

// SELECT * FROM users WHERE name = 'jinzhu' OR (name = 'jinzhu 2' AND age = 18);

// Map

db.Where("name = 'jinzhu'").Or(map[string]interface{}{"name": "jinzhu 2", "age": 18}).Find(&users)

// SELECT * FROM users WHERE name = 'jinzhu' OR (name = 'jinzhu 2' AND age = 18);

您还可以查看高级查询中的 分组条件,它被用于编写复杂 SQL

选择特定字段

选择您想从数据库中检索的字段,默认情况下会选择全部字段

db.Select("name", "age").Find(&users) // SELECT name, age FROM users;

db.Select([]string{"name", "age"}).Find(&users) // SELECT name, age FROM users;

db.Table("users").Select("COALESCE(age,?)", 42).Rows() // SELECT COALESCE(age,'42') FROM users; 还可以看一看 智能选择字段

Order

指定从数据库检索记录时的排序方式

db.Order("age desc, name").Find(&users)

// SELECT * FROM users ORDER BY age desc, name;

// Multiple orders

db.Order("age desc").Order("name").Find(&users)

// SELECT * FROM users ORDER BY age desc, name;

Limit & Offset

Limit 指定获取记录的最大数量 Offset 指定在开始返回记录之前要跳过的记录数量

db.Limit(3).Find(&users)

// SELECT * FROM users LIMIT 3;

// 通过 -1 消除 Limit 条件

db.Limit(10).Find(&users1).Limit(-1).Find(&users2)

// SELECT * FROM users LIMIT 10; (users1)

// SELECT * FROM users; (users2)

db.Offset(3).Find(&users)

// SELECT * FROM users OFFSET 3;

db.Limit(10).Offset(5).Find(&users)

// SELECT * FROM users OFFSET 5 LIMIT 10;

// 通过 -1 消除 Offset 条件

db.Offset(10).Find(&users1).Offset(-1).Find(&users2)

// SELECT * FROM users OFFSET 10; (users1)

// SELECT * FROM users; (users2)

查看 Pagination 学习如何写一个分页器

 Group & Having
 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)

// SELECT name, sum(age) as total FROM users WHERE name LIKE "group%" GROUP BY name

db.Model(&User{}).Select("name, sum(age) as total").Group("name").Having("name = ?", "group").Find(&result)

// SELECT name, sum(age) as total FROM users GROUP BY name HAVING name = "group"

rows, err := db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Rows()

从模型中选择不相同的值

Joins

指定 Joins 条件

type result struct { Name string Email string } db.Model(&User{}).Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Scan(&result{}) // SELECT users.name, emails.email FROM users left join emails on emails.user_id = users.id

rows, err := db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Rows() for rows.Next() { ... }

db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Scan(&results)

// 带参数的多表连接 db.Joins("JOIN emails ON emails.user_id = users.id AND emails.email = ?", "jinzhu@example.org").Joins("JOIN credit_cards ON credit_cards.user_id = users.id").Where("credit_cards.number = ?", "411111111111").Find(&user) Joins 预加载

First 的使用踩坑

使用First时,需要注意查询不到数据会返回

ErrRecordNotFound

使用Find查询多条数据,查询不到数据不会返回错误

使用结构体作为查询条件

当使用结构体作为条件查询时,GORM只会查询

非零值字段。这意味着如果您的字段值为0、“、

false或其他零值,这字段不会被用于构建查询条件,使用Map来构建查询条件