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()
}