gorm,kitex,hertz| 青训营

120 阅读3分钟

Gorm的基础使用

gorm基础使用

type Product struct {  
Code string  
Price uint  
}  
  
func (p Product) TableName() string {  
return "product"  
}  
func main() {  
db, err := gorm.Open(mysql.Open("user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"), &gorm.Config{})  
if err != nil {  
panic("failed to connect database")  
}  
//Create  
db.Create(&Product{Code: "D42", Price: 100})  
//read  
var product Product  
db.First(&product, 1)  
db.First(&product, "code = ?", "D42")  
//Update  
db.Model(&product).Update("Price", 200)  
//Update 更新多个字段  
db.Model(&product).Updates(Product{Price: 200, Code: "F42"}) //仅更新非0值  
db.Model(&product).Updates(map[string]interface{}{"Price": 200, "Code": "F42"}) //更新0值  
//Delete 删除product  
db.Delete(&product, 1)  
}

Gorm的约定

  • Gorm使用名为ID的字段作为主键
  • 如果没有定义TableName,使用结构体的蛇形负数作为表名
  • 字段名的蛇形作为列名
  • 使用CreatedAt、,UpdatedAt字段作为创建、更新时间
    GORM目前支持MySQL、SQLServer、PostgreSQL、SQLite
    Code string gorm:"column:code" 定义列名
    Code string gorm:"column:galeone"定义默认值
    创建多条数据
products := []*Product{{Code: "D41"}, {Code: "D42"}, {Code: "D43"}}  
db.Create(products)

Upsert使用clause.OnConflict处理数据冲突

p := &Product{Code: "D42", ID: 1}  
db.Clauses(clause.OnConflict{DoNothing: true}).Create(p)

Gorm查询数据

db.First()//查询一条数据
//查询多条数据
users := make([]*User, 0)  
db.Where("age > 10").Find(&users)
//In查询
db.Where("name IN ?",[]string{"jinzhu","jinzhu 2"}).Find(&users)
//结构体查询
db.Where(&User{Name: "jinzhu", Age: 0}).Find(&users)//0值不会被加入,map可以解决

First的使用踩坑

  • 使用First时,需要注意查询不到数据会返回ErrRecordNotFound。
  • 使用Find查询多条数据,查询不到数据不会返回错误。

使用结构体作为查询条件

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

Gorm更新数据

//更新单个列
db.Model(&User{ID:111}).Where("age>?",18)Update("name","helto")
//更新多个列
db.Model(&User{ID:111)).Updates(User(Name:"hello",Age:18))//如果没有where查询条件就会使用Model
//根据map更新属性
db.Model(&User(ID:111)1.Updates(map[string]interface(}{"name":"hello","age":18,"actived":false})
//更新选定字段
db.Model(&User{ID:111}).Select("name").Updates(map[string]interface{"name":"hello"
"age":18,"actived":false})//只会更新name
//sql表达式更新
db.Model(&User{ID:111}).Update("age",gorm.Expr(expr:"age*?+?",2,100))

使用Struct更新时,只会更 新非零值,如果需要更新零 值可以使用Map更新或使用 Select选择字段

GORM删除数据
物理删除

db.Delete(&User{},10)//DELETE FROM Users WHERE id 18;  
db.Delete(&User{},"10")//DELETE FROM users WHERE id 10;  
db.Delete(&User{},[]int{1,2,3})//DELETE FROM users WHERE id IN (1,2,3);  
db.Where("name LIKE ?","%jinzhux").Delete(User{})//DELETE from users where name LIKE "jinzhu";  
db.Delete (Use{},"email LIKE ?","%jinzhux")//DELETE from users where name LIKE "%jinzhux";

软删除
在代码里面额外定义一个Deleted gorm:DeletedAt字段
GORM提供了gorm.DeletedAt用于帮助用户实现软别 拥有软别除能力的Model调用Delete时,记录不会被从数 据库中真正别除。但GORM会将DeletedAt置为当前时间, 并且你不能再通过正常的查询方法找到该记录。 使用Unscoped可以查询到被软删的数据

GORM 事务
Gorm提供了Begin、Commit、Rollback方法用于使用事务

t×:=db.Begin()//开始事务
//在事务中执行一些db操作应该使用tx而不是db
tx.Rollback()//遇到错误回滚事务
tx.Commit()//提交事务

Gorm提供了Tansaction方法用于自动提交事务,避免用户漏写Commit、Rollbcak。

if err = db.Transaction(func(tx *gorm.DB) error {
		if err = tx.Create(&User{Name: "name"}).Error; err != nil {
			return err
		}
		if err = tx.Create(&User{Name: "name1"}).Error; err != nil {
			tx.Rollback()
			return err
		}
		return nil
	}); err != nil {
		return
	}

GORM HOOK

func(u *User) BeforeCreate(tx * gorm.DB)(err error){
		if u.Age <0 {
			return errors.New("can't save invalid data")
		}
		return
	}
	func(u *User) AfterCreate(tx * gorm.DB)(err error){
		return tx.Create(&Email{ID: u.ID, Email: u.Name "@***.com"}).Error
	}

GORM在提供了CURD的Hook能力。 Hook是在创建、查询、更新、删除等 操作之前、之后自动调用的函数。 如果任何Hook返回错误,GORM将 停止后续的操作并回滚事务

GORM性能提高

&gorm.Config{
SkipDefaultTransaction:true,//关闭默认事务
PrepareStmt:true},//缓存预编详语句

对于写操作(创建、更新、删除),为了确保数据的完整性,GORM会将它们封装在事务内运行。但这会降低性能,你可以使用SkipDefaultTransaction关闭默认事务。

使用PrepareStmt缓存预编译语句可以提高后续调用的速度,本机测试提高大约35%左右。

Kitex

定义IDL
使用DL定义服务与接口
如果我们要进行RPC,就需要 知道对方的接口是什么,需要传 什么参数,同时也需要知道返回 值是什么样的。这时候,就需要 通过IDL来约定双方的协议,就 像在写代码的时候需要调用某个 数,我们需要知道函数签名一 样

namespace go api
struct Request {
	1:string message
}
struct Response{
	1: string message
}
service Echo{
	Response echo(1: Request req)
}

生成代码 kitex -module example -service example echo.thrift 编写服务
默认监听8888端口

package main

import (
	"context"
	api "gorm/kitex/kitex_gen/api"
)

// EchoImpl implements the last service interface defined in the IDL.
type EchoImpl struct{}

// Echo implements the EchoImpl interface.
func (s *EchoImpl) Echo(ctx context.Context, req *api.Request) (resp *api.Response, err error) {
	// TODO: Your code here...
	return
}

创建Client

c,err :=echo.NewClient("example",client.WithHostPorts("0.0.0.0:8888"))//目标服务名在服务发现可以过滤服务
	if err !=nil {
		log.Fatal(err)
	}

发起请求

	req := &api.Request{Message: "my request"}
	resp, err := c.Echo(context.Background(), req, callopt.WithRPCTimeout(3*time.Second))
	if err != nil {
		log.Fatal(err)
	}
	log.Println(resp)

目前Kitex的服务注册与发现已经对接了主流了服务注册与发现中心,如ETCD,Nacos等。 服务注册

type HelloImpl struct {
}

func (h *HelloImpl) Echo(ctx context.Context, req *api.Response) (resp *api.Response, err error) {
	resp = &api.Response{
		Message: req.Message,
	}
	return
}
func main() {
	r, err := etcd.NewEtcdRegistry([]string{"127.0.0.1:2379"})
	if err != nil {
		log.Fatal(err)
	}
	server := hello.NewService(new(HelloImpl), server.WithRegistry(r), server.WithServerBasicInfo(&rpcinfo.EndpointBasicInfo{
		ServiceName: "Hello",
	}))
	err = server.Run()
	if err != nil {
		log.Fatal(err)
	}
}

服务发现

func main() {
	r, err :=etcd.NewEtcdResolver([]string{"127.0.0.1:2379"})
	if err != nil {
		log.Fatal(err)
	}
	client := hello.MustNewclient("Hello", client.WithResolver(r))//服务名过滤
	for {
		ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
		resp, err := client.Echo(ctx, &api.Request{Message: "Hello"})
		cancel()
		if err != nil {
			log.Fatal(err)
		}
		log.Println(resp)
		time.Sleep(time.Second)
	}
}

Hertz

Hertz基本使用

func main() {
	h := server.Default(server.WithHostPorts("127.0.0.1:8080"))
	h.GET("/ping", func(c context.Context, ctx *app.RequestContext) {
		ctx.JSON(consts.StatusOK, utils.H{"ping": "pong"})
	})
	h.Spin()
}

Hetz提供了GET、POST、PUT、DELETE、ANY等方法用于注册路由
h.Group("/v1")路由分组
Hetz提供了参数路由和通配路由,路由的优先级为:静态路由>命名路由>通配路由
:version参数路由
*action通配路由(可以匹配多段)
Hertz参数绑定
Hertz提供了Bind、Validate、BindAndValidate函数用于进行参数绑定和校验

type Args struct {
	Query      string   `query:"query"`
	QuerySlice []string `query:"q"`
	Path       string   `path:"path"`
	Header     string   `header:"header"`
	Form       string   `json:"json"`
	Json       string   `json:"json"`
	Vd         int      `query:"vd" vd:"$==0||$==1"`
}

func main() {
	h := server.Default(server.WithHostPorts("127.0.0.1:8080"))
	h.GET("/ping", func(c context.Context, ctx *app.RequestContext) {
		ctx.JSON(consts.StatusOK, utils.H{"ping": "pong"})
	})
	h.POST("v:path/bind", func(c context.Context, ctx *app.RequestContext) {
		var arg Args
		err := ctx.BindAndValidate(&arg)
		if err != nil {
			panic(err)
		}
		fmt.Println(arg)
	})
	h.Spin()
}