package
0.0.0-20250122034743-c126e93bce09
Repository: https://github.com/walleframe/pb_plugins.git
Documentation: pkg.go.dev

# README

#+startup: content #+title: mysql生成插件

  • protoc-gen-mysql 通过定义一个protobuf message. 来生成对应的mysql操作管理代码(避免使用反射).生成代码使用 sqlx.DB 进行数据库操作.

v0.0.2 拆分输出文件 将表操作独立定义包,用于简化代码提示. 支持mysql事务.

同数据库定义的package名应该相同.同一个数据库生成在一个代码包内.每个表生成一个源码文件.


可以替换自己的管理包,来实现自定义的配置管理,链接管理等.


表自动同步:

#+begin_quote 生成代码默认会在服务启动时候,检测表和索引. 修改 svc_db.SyncTableColumnsAndIndex=false 来禁用此功能 #+end_quote

  • 检测数据库内表是否存在,不存在则创建. 如果表已经存在,会检查字段,如果有新增字段,自动添加缺失的字段.
  • 检测数据库内表所关联的索引是否存在,不存在则创建.

生成代码中以下接口使用 sql.Stmt. 批量接口和其余接口都是拼接sql.

  • insert
  • update
  • upsert
  • find
  • delete

只有设置了Primary Key字段, 才会生成update,upsert,find 相关方法.否则可以使用select配合where使用进行查询,使用insert 进行更新.

生成代码对外暴露了 *sqlx.DB 指针,可以自定义执行的sql.

生成代码还包含了一个简单的sql语句生成(会根据调用顺序生成sql语句,应该像正常写sql那样去调用),例: #+begin_src go // insert user_info(uid,email,name) values(:uid,:email,:name) user_info_def.NamedSQL(128).Insert().Uid().Email().Name().ToSQL()

// delete from user_info where email = :email and name = :name user_info_def.NamedSQL(128).Delete().Email().And().Name().ToSQL()

// update user_info set name=:name,email=:email where uid = :uid user_info_def.NamedSQL(128).Update().Name().Email().Where().Uid().ToSQL()

//select uid,name from user_info where uid = :uid and email = :email limit 10,0 user_info_def.NamedSQL(128).Select().Uid().Name().Where().Uid().And().Email().Limit(10, 0).ToSQL()

// 错误的示例(ERROR EXAMPLE): => update user_info set email=:email where and email = :email user_info_def.NamedSQL(128).Update().Email().Where().And().Email().ToSQL() #+end_src #+begin_quote sql语句生成建议使用包内变量形式 #+end_quote #+begin_src go var querySomething = user_info_def.NamedSQL(128).Select().Uid().Name().Where().Uid().And().Email().Limit(10, 0).ToSQL() #+end_src

** 基础配置信息 | 描述 | 环境变量 | 命令行参数 | 默认值 | |--------------+-------------------+------------+----------------------------------------------| | 服务管理包 | MYSQL_PKG_SVC | pkg_svc | github.com/walleframe/svc_db | | 工具包 | MYSQL_PKG_UTIL | pkg_util | github.com/walleframe/walle/util | | | MYSQL_COLLATE | collate | utf8mb4_general_ci | | | MYSQL_CHARSET | charset | utf8mb4 | | 代码路径 | MYSQL_OPCODE_PATH | code_path | pkg/gen/mysqlop | | 生成代码包名 | MYSQL_OPCODE_PKG | code_pkg | github.com/walleframe/svc_db/pkg/gen/mysqlop |

uitl包

  • 主要使用 util.Buffer 来进行字符串格式化(为了在运行期间尽量少的申请内存).

svc管理包

  • 使用 RawTo... , AnyFrom... 等方法简化生成代码逻辑.
  • 使用 RegisterSyncDBTable , RegisterDB 等管理数据库.

配置优先级:

  • 表级别选项设置
  • 文件级别选项配置
  • 命令行参数
  • 环境变量
  • 默认值 ** 选项配置 *** 文件级别 | 名称 | 类型 | 说明 | |------------------+--------+----------------------------------------------------| | mysql.db_name | string | 数据库名. 不设置此字段,生成插件会忽略当前proto文件 | | mysql.db_charset | string | | | mysql.db_collate | string | |

*** 消息级别 | 名称 | 类型 | 默认值 | 说明 | |-------------------+-------------------+--------------+-------------------------------------------------| | mysql.tbl_name | string | tbl_[消息名] | 数据库表名 | | mysql.ignore | bool | false | 是否忽略当前消息,不生成数据库表 | | mysql.engine | string | InnoDB | 数据库引擎 | | mysql.pks | string | | 设置primary key,值为以’,’分隔的字段名称 | | mysql.unique | string | | unique index. name(column,...);name(column,...) | | mysql.index | string | | table index. name(column,...);name(column,...) | | mysql.update | bool | true | 是否生成update方法 | | mysql.upsert | bool | true | 是否生成upsert方法 | | mysql.gen_ex | bool | false | 生成扩展消息接口(如果已经禁用扩展,忽略此字段) | | mysql.tbl_opt | string | | 添加自定义数据库表选项 | | mysql.tbl_charset | string | | | | mysql.tbl_collate | string | | | | mysql.unique_v2 | mysql.Index | | 更清晰的表示方式 | | mysql.index_v2 | mysql.Index | | 更清晰的表示方式 | | mysql.duplication | mysql.Duplication | | 重复生成相同结构体的表 |

**** mysql.index/mysql.unique/mysql.index_v2/mysql.unique_v2

name的值不是最终的索引名, 最终的索引名称: [idx/uniq][sql_table_name][name]

name如果是驼峰命名,会转换成小写字符加下划线的格式. 大写字母全部会替换成小写的.

所有的name不能相同,工具会辅助检查

不同的索引的合理性由使用者保证,工具不会做检测

mysql.indexmysql.unique 都是使用字符串拼接,格式是 name(column,...);name(column,...)

mysql.index_v2mysql.unique_v2 都是使用结构体 mysql.Index. 如下. #+begin_src protobuf message Index { string name = 1; repeated string columns = 2; } #+end_src 使用示例: #+begin_src protobuf option (mysql.index_v2) = { name: "uk3", columns: [ "email", "name", "visitor_id" ] }; #+end_src 需要建立多个索引就写多个配置.

mysql.Duplication #+begin_src protobuf message Duplication { string name = 1 [retention = RETENTION_SOURCE]; string tbl_name = 2 [retention = RETENTION_SOURCE]; } #+end_src

**** mysql.gen_ex 扩展字段: #+begin_src sql modify_stamp timestamp default current_timestamp on update current_timestamp create_stamp timestamp default current_timestamp #+end_src

支持生成扩展消息查询,生成的sql表不会自动添加扩展字段.

默认情况下, 如果想查询扩展字段,请定义一个 名字带Ex的消息体. 在原结构基础上添加 modify_stamp,create_stamp字段.

例: 原消息是 message Abc. 需要定义 message AbcEx.

*** 字段级别 | 名称 | 类型 | 默认值 | 说明 | |-----------------+--------+--------+-------------------------------------| | mysql.pk | bool | false | | | mysql.increment | bool | false | auto_increment | | mysql.type | string | | 手动指定数据库字段类型 | | mysql.size | int32 | 64 | 对string类型设置长度,varchar(size) | | mysql.custom | bool | false | 对本字段,自定义序列化和反序列化函数 | | mysql.column | string | | 自定义字段设置,全部都需要手动写 | **** mysql.column 手动指定column的全部信息.

设置后忽略 mysql.pk , mysql.increment , mysql.type, mysql.size **** mysql.type 只写mysql数据类型,更多定制请采用 mysql.column ** 生成说明 关于生成的接口:

  • 所有db都会生成的接口
    1. Insert
    2. InsertMany
    3. Select 使用自定义sql查询数据(建议使用 [表名]SQL_Find 拼接sql语句,只需要写where语句部分就可以)
    4. Count 使用自定义sql查询数量
    5. DeleteMany 使用自定义sql删除数据(建议使用 [表名]SQL_Find 拼接sql语句,只需要写where语句部分就可以)
    6. RangeAll 使用自定义sql查询数据,处理多条数据,传递匿名函数进行依次处理
    7. AllData 使用自定义sql查询数据,返回多条数据(返回数组)
    8. DB() *sqlx.DB 获取数据库对象,用于自定义执行sql.
  • 必须设置PrimaryKey才会生成的接口
    1. Update 并且 mysql.update / mysql.upsert 为true(默认是true)
    2. Upsert/UpsertMany方法 并且 mysql.upsert 为true(默认是true).
    3. Find/Delete 使用主键查找/删除数据库记录,处理单条记录
    4. FindByKey/DeleteByKey 同(Find/Delete). 区别在于当有多个字段作为PrimaryKey时候, 会生成 [表名]Key 结构体.
    5. FindByKeyArray/DeleteByKey 操作多条数据
  • 有普通索引才会生成的接口
    1. FindByIndex[IndexName] 返回多条数据
    2. CountByIndex[IndexName]
    3. DeleteByIndex[IndexName]
  • 唯一性索引
    1. FindByIndex[IndexName] 返回单条数据
    2. DeleteByIndex[IndexName]

辅助方法:

  • New[表名]Operation 新建数据库表对象.
  • [表名]OP 函数,用于获取数据库表对象. 实际实现 使用变量, 方便后续调试和测试直接替换函数. 例: #+begin_src go var UserOP = func() UserOperation { op := globalUserOP.Load() if op == nil { return nil } return op } #+end_src
  • Sync[表名]DBTable 用于同步数据库表结构和索引 生成代码默认会在服务启动时候,检测表和索引. 修改 svc_db.SyncTableColumnsAndIndex=false 来禁用此功能
  • [表名_def].NamedSQL 辅助生成sql语句.
  • [表名_def].SelectWhere / [表名_def].CountWhere / [表名_def].DeleteWhere 自定义拼接sql语句辅助. 使用?占位符.

辅助常量和变量:

  • [表名_def].SQL_Create 常量 创建sql表语句
  • [表名_def].SQL_TableColumns map变量. 修改表添加某个字段的语句.
  • [表名_def].SQL_TableIndex map变量. 创建索引的sql语句.
  • [表名_def].SQL_Find / [表名_def].SQL_Count / [表名_def].SQL_Delete 辅助语句, 自定义查询时候可以使用.

** 代码示例 #+begin_src protobuf option (mysql.db_name) = "db_user";

message User { option (mysql.tbl_name) = "user"; // option (mysql.engine) = "InnoDB"; option (mysql.tbl_charset) = "utf8mb4"; option (mysql.tbl_collate) = "utf8mb4_0900_ai_ci"; // option (mysql.pks) = "id"; option (mysql.engine) = "MyISAM"; option (mysql.unique) = "name(name)"; option (mysql.index) = "email(email,password);visitor_id(visitor_id)"; option (mysql.index_v2) = { name: "uk2", columns: [ "email", "name", "visitor_id" ] };

int64  id          = 1 [(mysql.pk) = true, (mysql.increment) = true];
int32  state       = 2;
int64  login_time  = 3;
string email       = 4;
string password    = 5;
string login_ip    = 6;
string birthday    = 7;
string notionality = 8;
string name        = 9;
string avatar      = 10;
int32  user_source = 11;
string visitor_id  = 12;
int64  created_at  = 13;
int64  updated_at  = 14;
int32  verified    = 15 [(mysql.type) = "tinyint not null defaULT 0"];

} #+end_src

生成接口 #+begin_src go package db_user

type UserKey = int64

type UserOperation interface { Insert(ctx context.Context, data *mysqlop.User) (res sql.Result, err error) InsertMany(ctx context.Context, datas []*mysqlop.User) (res sql.Result, err error)

Update(ctx context.Context, data *mysqlop.User) (res sql.Result, err error)
Upsert(ctx context.Context, data *mysqlop.User) (res sql.Result, err error)
UpsertMany(ctx context.Context, datas []*mysqlop.User) (res sql.Result, err error)

Find(ctx context.Context, id int64) (data *mysqlop.User, err error)
Delete(ctx context.Context, id int64) (res sql.Result, err error)

FindByKey(ctx context.Context, id UserKey) (data *mysqlop.User, err error)
DeleteByKey(ctx context.Context, id UserKey) (res sql.Result, err error)

FindByKeyArray(ctx context.Context, ids []UserKey) (datas []*mysqlop.User, err error)
DeleteByKeyArray(ctx context.Context, ids []UserKey) (res sql.Result, err error)

FindByIndexEmail(ctx context.Context, email string, password string, limit, offset int) (datas []*mysqlop.User, err error)
CountByIndexEmail(ctx context.Context, email string, password string) (count int, err error)
DeleteByIndexEmail(ctx context.Context, email string, password string) (res sql.Result, err error)

FindByIndexVisitorId(ctx context.Context, visitor_id string, limit, offset int) (datas []*mysqlop.User, err error)
CountByIndexVisitorId(ctx context.Context, visitor_id string) (count int, err error)
DeleteByIndexVisitorId(ctx context.Context, visitor_id string) (res sql.Result, err error)

FindByIndexName(ctx context.Context, name string) (datas *mysqlop.User, err error)
DeleteByIndexName(ctx context.Context, name string) (res sql.Result, err error)

FindByIndexUk3(ctx context.Context, email string, name string, visitor_id string, limit, offset int) (datas []*mysqlop.User, err error)
CountByIndexUk3(ctx context.Context, email string, name string, visitor_id string) (count int, err error)
DeleteByIndexUk3(ctx context.Context, email string, name string, visitor_id string) (res sql.Result, err error)

FindByIndexUk2(ctx context.Context, email string, name string, visitor_id string) (datas *mysqlop.User, err error)
DeleteByIndexUk2(ctx context.Context, email string, name string, visitor_id string) (res sql.Result, err error)

Select(ctx context.Context, findSql string, args ...any) (datas []*mysqlop.User, err error)
Count(ctx context.Context, findSql string, args ...any) (count int, err error)

DeleteMany(ctx context.Context, findSql string, args ...any) (res sql.Result, err error)

RangeAll(ctx context.Context, findSql string, oneQueryLimit int, f func(ctx context.Context, row *mysqlop.User) bool, args ...any) error
AllData(ctx context.Context, findSql string, oneQueryLimit int, args ...any) (datas []*mysqlop.User, err error)


// use for custom named sql
DB() *sqlx.DB

}

var UserOP = func() UserOperation #+end_src

使用代码 #+begin_src go func xx(ctx context.Context){ userInfo, err := db_user.UserOP().Find(ctx, 123) // .... }

// 以下三条语句语义是相等的 var ( query0 = "select id,state,login_time,email,password,name,avatar,created_at,updated_at, from user where id = ? and name = ?" query1 = user_def.SelectWhere(128).Id().Equal().And().Name().Equal().String() query2 = user_def.SQL_Find + " where id = ? and name = ?" ) #+end_src

# Variables

No description provided by the author