GORM入门 | 青训营笔记

79 阅读2分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 6 天。 本篇笔记记录我入门使用GORM的一些过程。

本篇文章接续上次的创作。Hertz入门 | 青训营笔记 - 掘金 (juejin.cn)

更复杂的接口

上次我们只有一个/echo接口,它的使用也不包含持久性的数据,本篇文章中我们添加两个新的接口,/note/add/note/list,分别用来向数据库中添加一条文本信息,以及列出全部的文本信息。

// idl/note/note.proto
syntax = "proto3";

package note;

option go_package = "note";

import "api.proto";

enum Code {
    NoCode = 0;
    Success = 1;
    ParamInvalid = 2;
    DBErr = 3;
}

message Note {
    int64 id = 1;
    string content = 2;
}

message NoteAddReq {
    string content = 1 [(api.body) = "content", (api.form) = "content", (api.vd) = "(len($) > 0 && len($) < 100)"];
}

message NoteAddResp {
    Code statusCode = 1 [(api.body) = "status_code"];
}

service NoteAddService {
    rpc NoteAdd(NoteAddReq) returns(NoteAddResp) {
        option (api.post) = "/note/add";
    }
}

message NoteListReq {
    int64 maxNum = 1 [(api.query) = "max", (api.vd) = "$ > 0 && $ < 20)"];
}

message NoteListResp {
    Code statusCode = 1 [(api.body) = "status_code"];  
    repeated Note notes = 2; 
}

service NoteListService {
    rpc NoteList(NoteListReq) returns(NoteListResp) {
        option (api.get) = "/note/list";
    }
}

使用以下命令更新生成的代码

hz update -I idl -idl idl/note/note.proto

编写ORM代码生成脚本

创建cmd目录,并在该目录下创建generate.go文件,编写以下内容。

package main

import (
	"gorm.io/driver/sqlite"
	"gorm.io/gen"
	"gorm.io/gorm"
	"hertz-echo/biz/model/note"
)

func main() {
	g := gen.NewGenerator(gen.Config{
		OutPath: "../biz/query",
		Mode:    gen.WithoutContext | gen.WithDefaultQuery | gen.WithQueryInterface, // generate mode
	})

	// gormdb, _ := gorm.Open(mysql.Open("root:@(127.0.0.1:3306)/demo?charset=utf8mb4&parseTime=True&loc=Local"))
	gormdb, _ := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{})
	g.UseDB(gormdb) // reuse your gorm db

	// Generate basic type-safe DAO API for struct `model.User` following conventions
	g.ApplyBasic(note.Note{})

	// Generate the code
	g.Execute()
}

运行生成脚本,生成相关的基础文件。

cd cmd & go run generate.go

可以看到新生成的文件 image.png

编写业务逻辑

// NoteAdd .
// @router /note/add [POST]
func NoteAdd(ctx context.Context, c *app.RequestContext) {
	var err error
	var req note.NoteAddReq
	resp := new(note.NoteAddResp)

	err = c.BindAndValidate(&req)
	if err != nil {
		resp.StatusCode = note.Code_ParamInvalid
		return
	}

	err = query.Note.Create(&ormNote.Note{Content: req.Content})
	if err != nil {
		resp.StatusCode = note.Code_DBErr
		return
	}

	resp.StatusCode = note.Code_Success

	c.JSON(consts.StatusOK, resp)
}
// NoteList .
// @router /note/list [GET]
func NoteList(ctx context.Context, c *app.RequestContext) {
	var err error
	var req note.NoteListReq
	resp := new(note.NoteListResp)

	err = c.BindAndValidate(&req)
	if err != nil {
		resp.StatusCode = note.Code_ParamInvalid
		return
	}

	modelNotes, err := query.Note.Order(query.Note.ID).Limit(int(req.MaxNum)).Find()
	if err != nil {
		resp.StatusCode = note.Code_DBErr
		return
	}

	notes := make([]*note.Note, 0, len(modelNotes))
	for _, m := range modelNotes {
		notes = append(notes, &note.Note{Id: m.ID, Content: m.Content})
	}
	resp.StatusCode = note.Code_Success
	resp.Notes = notes
	c.JSON(consts.StatusOK, resp)
}

有了自动生成的框架和orm代码,业务逻辑的编写就非常简单了。

注意,还需要在主程序中插入数据库初始化的代码 image.png

运行与测试

重新编译并运行程序 image.png

使用ApiPost工具来测试我们的接口

add接口测试 image.png

list接口测试 image.png