gograme学习

文章目录

  • 框架设计
    • 结构化输入输出
    • 数据模型
    • 隐式初始化与显式初始化
    • context
  • Quickstart
  • 日志管理
  • 错误处理
  • 数据校验
  • 缓存管理
  • 数据库ORM
    • orm 时间相关方法
    • 数据库升级脚本
    • 默认值
    • 模型创建
    • 事务处理
  • 微服务
    • 其他常识

gograme学习_第1张图片

框架设计

结构化输入输出

输出输出用结构体来定义结构,避免在方法中硬编码,并且方便生成接口文档

数据模型

包括MySQL、Redis、MongoDB、Kafka在内的持久化数据库的数据结构,是框架自动生成的,在/app/dao/internal/model/entity目录下

隐式初始化与显式初始化

main.go里import的包里自带的init方法是隐式初始化,goframe框架的很多模块使用隐式初始化;boot/boot.go里调用的方法是显式初始化吗,一般是与业务相关的用显式初始化

context

在一次请求中,协程共享的变量,类似于java的threadlocal

Quickstart

安装swagger / 更新swagger文档

gf swagger --pack

更改安装的swagger版本

go get -u github.com/swaggo/swag/cmd/[email protected] (通过go.mod来执行的)

go get xxx.cn/gaea-server/x/core@master 来拉取分支
也可以来更新goframe版本
go get -u github.com/gogf/gf/v2

删除安装模块的缓存:
go clean --modcache

日志管理

没啥要看的

错误处理

没有用gerror,还是原生的error处理

数据校验

类似Laravel里面的validator : p - params , v - valid,项目里没有自定义错误

缓存管理

有gcache但是主要还是配置的redis
因为ORM的*gcache.Cache缓存对象提供的是单进程内存缓存,如果服务采用多节点部署,多节点之间的缓存可能会产生数据不一致的情况,因此通过Redis服务器来实现对数据库查询数据的缓存,将单进程内存缓存改为分布式的Redis缓存

fileDb.GetCache().SetAdapter(redis.NewRedis(redis2.DefaultClient()))

数据库ORM

能够自动生成DAO代码
gf gen dao -path ./app/ -c config/config.yml -g fic(group名,可见.sql的上层文件夹名) -t user_view_menu(表名) -modelFile test.go(输出文件名)

yaml配置文件里,放了两个数据库分组的配置信息,由于没有配置主从,所有读写都是在master节点上执行

orm 时间相关方法

.WhereLT(dao.EntExportQueue.Columns.CreatedAt, time.Now().UTC().Add(-7*24*time.Hour))

可是查出7天前数据

数据库升级脚本

存储过程示例

-- 创建删除索引的存储过程
DROP PROCEDURE IF EXISTS del_index;
CREATE PROCEDURE del_index(IN target_table_name VARCHAR(100),IN target_index_name VARCHAR(100))
BEGIN
    IF EXISTS (SELECT * FROM information_schema.statistics WHERE table_schema = DATABASE()  AND table_name = target_table_name AND index_name = target_index_name) THEN
        set @statement =( select concat ('alter table ',target_table_name, ' drop key ',target_index_name ));
        PREPARE STMT FROM @statement;
        EXECUTE STMT;
    END IF;
END;

call del_index('tag','name');
call del_index('tag','idx_name_ttype');

存储过程参考 https://blog.csdn.net/scdncby/article/details/125738444

-- t 表名;i 索引名称;v 创建索引执行的sql ddl语句
-- 创建删除索引的存储过程
DROP PROCEDURE IF EXISTS del_index;
DELIMITER $
CREATE PROCEDURE del_index(IN t VARCHAR(100),IN i VARCHAR(100),IN v VARCHAR(255))
BEGIN
DECLARE  target_database VARCHAR(100);
DECLARE  target_table_name VARCHAR(100);
DECLARE  target_column_name VARCHAR(100);
DECLARE  target_index_name VARCHAR(100);
set target_table_name = t;
set target_index_name = i;
SELECT DATABASE() INTO target_database;
IF EXISTS (SELECT * FROM information_schema.statistics WHERE table_schema = target_database AND table_name = target_table_name AND index_name = target_index_name) THEN
    set @statement = v;
    PREPARE STMT FROM @statement;
    EXECUTE STMT;
END IF;
END;
$
DELIMITER ;

call del_index('tag','name',"alter table tag drop key name");
call del_index('tag','name',"alter table tag drop key idx_name_ttype");

实用sql

# 查询表索引
SELECT * FROM information_schema.statistics WHERE table_schema = DATABASE()  AND table_name = 'tag';
# 查数据库mode
select @@session.sql_mode;

默认值

OmitEmptyData()让框架忽略零值
0, nil, false, "", len(slice/map/chan) == 0,插入数据库默认值
所以禁止在数据库里填默认值为非0的,以防想写入0却被orm忽略插入数据库默认值;在数据库里默认为0,业务逻辑里写非0的默认值,在数据库里没有该条数据的时候直接返回默认值。

oMitEmptyData()会忽略model里没填入的字段,但是会导致无法将某字段更新为0值,此时只能先查出旧的model数据,指定更新某个字段,并且不使用oMitEmptyData()
会忽略掉deleted有值的字段即sql默认加上where deletedAt is not null; 加上Unscoped()就不加上判断deleted的字段

模型创建

Model方法用于创建基于数据表的Model对象

g.DB().Model("user")   // g代表一个DAO对象

由于链式操作的每一个方法会对Model属性进行修改,造成Model对象不能重复使用

user := g.Model("user")
user.Where("status", g.Slice{1,2,3})
if vip {
    // 查询条件自动叠加,修改当前模型对象
    user.Where("money>=?", 1000000)
} else {
    // 查询条件自动叠加,修改当前模型对象
    user.Where("money,  1000000)
}
//  vip: SELECT * FROM user WHERE status IN(1,2,3) AND money >= 1000000
// !vip: SELECT * FROM user WHERE status IN(1,2,3) AND money < 1000000
r, err := user.All()
//  vip: SELECT COUNT(1) FROM user WHERE status IN(1,2,3) AND money >= 1000000
// !vip: SELECT COUNT(1) FROM user WHERE status IN(1,2,3) AND money < 1000000
n, err := user.Count()

所以通过Safe方法对每一个链式操作都返回一个新的Model操作,该Model对象可以重复使用。(项目中就用的这种方式)

// 定义一个用户模型单例
user := g.Model("user").Safe()
m := user.Where("status", g.Slice{1,2,3})
if vip {
    // 查询条件通过赋值叠加
    m = m.And("money>=?", 1000000)
} else {
    // 查询条件通过赋值叠加
    m = m.And("money,  1000000)
}
//  vip: SELECT * FROM user WHERE status IN(1,2,3) AND money >= 1000000
// !vip: SELECT * FROM user WHERE status IN(1,2,3) AND money < 1000000
r, err := m.All()
//  vip: SELECT COUNT(1) FROM user WHERE status IN(1,2,3) AND money >= 1000000
// !vip: SELECT COUNT(1) FROM user WHERE status IN(1,2,3) AND money < 1000000
n, err := m.Count()

事务处理

用闭包方式来使用事务

func (db DB) Transaction(ctx context.Context, f func(ctx context.Context, tx *TX) error) (err error)

func (dao *UserViewMenuDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) {
return dao.Ctx(ctx).Transaction(ctx, f)
}
是一种事务使用方式吗??(没看到哪个方法使用到了),反正只在web项目中看到只有2个方法显示用到了事务.Transaction
嵌套事务????

微服务

每个功能抽成服务,服务间通过rpc来访问
rpc示例:
服务端,注册服务

package main

import (
    "fmt"
    "log"
    "net"
    "net/rpc"
    "net/rpc/jsonrpc"
)

type Params struct {
    Width, Height int
}
type Rect struct {
}

func (r *Rect) Area(p Params, ret *int) error {
    *ret = p.Width * p.Height
    return nil
}
func (r *Rect) Perimeter(p Params, ret *int) error {
    *ret = (p.Height + p.Width) * 2
    return nil
}
func main() {
    rpc.Register(new(Rect))
    lis, err := net.Listen("tcp", ":8080")
    if err != nil {
        log.Panicln(err)
    }
    for {
        conn, err := lis.Accept()
        if err != nil {
            continue
        }
        go func(conn net.Conn) {
            fmt.Println("new client")
            jsonrpc.ServeConn(conn)
        }(conn)
    }
}

客户端,调用服务

package main

import (
    "fmt"
    "log"
    "net/rpc/jsonrpc"
)

type Params struct {
    Width, Height int
}

func main() {
    conn, err := jsonrpc.Dial("tcp", ":8080")
    if err != nil {
        log.Panicln(err)
    }
    ret := 0
    err2 := conn.Call("Rect.Area", Params{50, 100}, &ret)
    if err2 != nil {
        log.Panicln(err2)
    }
    fmt.Println("面积:", ret)
    err3 := conn.Call("Rect.Perimeter", Params{50, 100}, &ret)
    if err3 != nil {
        log.Panicln(err3)
    }
    fmt.Println("周长:", ret)
}

其他常识

go mod tidy后go.mod还是标红,用go clean --modcache解决;

go.mod不标红,但是代码中有标红,清ide缓存
gograme学习_第2张图片

你可能感兴趣的:(Golang,学习)