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