![在这里插入图片描述](https://img-blog.csdnimg.cn/20210124204628149.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxMDY2MDY2,size_16,color_FFFFFF,t_70#pic_center)
今天是【7天从零实现TORM框架】的第三天,主要任务是:
- 利用 generators 类构建关键词SQL语句,代码100行左右。
- 利用 Clause 类构建条件组件,120行左右。
若对Go中反射的使用不了解的话,我写了三篇关于反射的文章,给小伙伴提供参考,足以应对本项目中所使用的反射知识点。
go反射第一弹:[https://mp.weixin.qq.com/s/F8yZyqC5UwoewsX0THqy1w](https://mp.weixin.qq.com/s/F8yZyqC5UwoewsX0THqy1w)
go反射第二弹:[https://mp.weixin.qq.com/s/lgZykTL8ls6aG0OMNSbZMw](https://mp.weixin.qq.com/s/lgZykTL8ls6aG0OMNSbZMw)
go反射第三弹:[https://mp.weixin.qq.com/s/vFt06c9herwTrx1LTxNaKg](https://mp.weixin.qq.com/s/vFt06c9herwTrx1LTxNaKg)
>源代码:在【迈莫coding】中回复关键字「 torm 」获取github地址链接.
后续会为【七天从零实现TORM框架】录制视频,文章+视频+代码
# generators构建关键词SQL语句
一条完整的sql语句是由多条子sql语句组装而成的,比如新增sql数据,格式如下所示:
```go
INSERT INTO user(user_name, age) VALUES("迈莫coding", 1);
```
将一条新增sql语句进行拆分的话,它分为两部分,一部分为 INSERT 关键词sql语句,另一部分是 VALUES 关键词sql语句。这样就把一条完整SQL语句进行拆分存储,接下来就会讲解这一步代码实现思路。
该部分代码存储在根目录下的 generators.go 文件中。
```go
// generators.go
package session
import (
"fmt"
"strings"
)
// 该类旨于 完成关键字SQL语句构建工作
type generator func(values ...interface{}) (string, []interface{})
var generators map[Type]generator
func init() {
generators = make(map[Type]generator)
generators[Insert] = _insert
generators[Value] = _values
generators[Update] = _update
generators[Condition] = _condition
generators[Delete] = _delete
generators[Limit] = _condition
generators[Select] = _select
generators[Where] = _where
}
// insert关键词
func _insert(values ...interface{}) (string, []interface{}) {
tableName := values[0]
fields := strings.Join(values[1].([]string), ",")
return fmt.Sprintf("INSERT INTO %s (%v)", tableName, fields), []interface{}{}
}
func genBindVars(num int) string {
var vars []string
for i := 0; i < num; i++ {
vars = append(vars, "?")
}
return strings.Join(vars, ",")
}
// values关键词
func _values(values ...interface{}) (string, []interface{}) {
var bindStr string
var sql strings.Builder
var vars []interface{}
sql.WriteString("VALUES ")
for i, value := range values {
v := value.([]interface{})
if bindStr == "" {
bindStr = genBindVars(len(v))
}
sql.WriteString(fmt.Sprintf("(%v)", bindStr))
if i+1 != len(values) {
sql.WriteString(",")
}
vars = append(vars, v...)
}
return sql.String(), vars
}
// 查询条件组装
func _condition(values ...interface{}) (string, []interface{}) {
var sql strings.Builder
sql.WriteString("`")
sql.WriteString(values[0].(string))
sql.WriteString("`")
sql.WriteString(values[1].(string))
sql.WriteString("?")
return sql.String(), []interface{}{values[2]}
}
// update关键词
func _update(values ...interface{}) (string, []interface{}) {
tableName := values[0]
m := values[1].(map[string]interface{})
var keys []string
var vars []interface{}
for k, v := range m {
keys = append(keys, k+"=?")
vars = append(vars, v)
}
return fmt.Sprintf("UPDATE %s SET %s", tableName, strings.Join(keys, ",")), vars
}
// delete关键词
func _delete(values ...interface{}) (string, []interface{}) {
return fmt.Sprintf("DELETE FROM %s", values[0]), []interface{}{}
}
// limit关键词
func _limit(values ...interface{}) (string, []interface{}) {
return "LIMIT ?", values
}
// select关键词
func _select(values ...interface{}) (string, []interface{}) {
return fmt.Sprintf("select %s from %s", values[0], values[1]), []interface{}{}
}
// where关键词
func _where(values ...interface{}) (string, []interface{}) {
return fmt.Sprintf("%s","WHERE"), []interface{}{}
}
```
代码说明
- generators 类存储各个关键词的生成规则。
# clause构建条件组件
然后在 clause.go 文件中去定义条件结构,比如表名、查询字段、修改字段、关键词sql语句等等,结构如下所示:
```go
// clause.go
package session
import (
"fmt"
"reflect"
"strings"
)
// 条件组装 不外显 便于与用户API分层隔离
type Clause struct {
cselect string // 查询字段
cset string // 修改字段
tablename string // 表名
condition string // 查询条件
limit int32
offset int32
sql string // 完整sql语句
params []interface{} // 参数
sqlType map[Type]string // key:关键词 values:关键词sql语句
paramsType map[Type][]interface{} // key:关键词 values:关键词sql语句
}
// NewClause 初始化
func newClause() *Clause {
return &Clause{
cselect: "*",
limit: -1,
offset: -1,
sqlType: make(map[Type]string),
paramsType: make(map[Type][]interface{}),
}
}
```
代码说明:
- 定义一个 Clause 结构体,里面存储条件信息,比如查询字段cselect,修改字段cset,表名tablename,完整sql语句sql,查询参数params等。
- newClause 方法进行初始化对象。
接下来需要将各个关键词sql语句拼接。
```go
// clause.go
type Type int
type Operation int
const (
Insert Type = iota
Value
Update
Delete
Limit
Select
Where
Condition
)
// SetTableName 设置表名
func (c *Clause) SetTableName(tableName string) *Clause {
c.tablename = tableName
return c
}
//
func (c *Clause) insertStruct(vars interface{}) *Clause {
types := reflect.TypeOf(vars)
if types.Kind() == reflect.Ptr {
types = types.Elem()
}
if types.Kind() != reflect.Struct {
return c
}
// 数据映射
schema := StructForType(types)
// 构建SQL语句
c.Set(Insert, c.tablename, schema.FieldNames)
recordValues := make([]interface{}, 0)
recordValues = append(recordValues, schema.RecordValues(vars))
c.Set(Value, recordValues...)
c.Build(Insert, Value)
return c
}
//
func (c *Clause) updateStruct(vars interface{}) *Clause {
types := reflect.TypeOf(vars)
if types.Kind() == reflect.Ptr {
types = types.Elem()
}
if types.Kind() != reflect.Struct {
return c
}
// 数据映射
schema := StructForType(types)
m := make(map[string]interface{})
m = schema.UpdateParam(vars)
// 构建SQL语句
c.Set(Update, c.tablename, m)
return c
}
func (c *Clause) andEqual(field string, value interface{}) *Clause {
return c.setCondition(Condition, "AND", field, "=", value)
}
func (c *Clause) orEqual(field string, value interface{}) *Clause {
return c.setCondition(Condition, "OR", field, "=", value)
}
// 查询字段
func (c *Clause) selectField(cselect ...string) *Clause {
c.cselect = strings.Join(cselect, ",")
return c
}
// 查询条件组装
func (c *Clause) setCondition(values ...interface{}) *Clause {
sql, vars := generators[values[0].(Type)](values[2:]...)
c.params = append(c.params, vars...)
c.addCondition(sql, values[1].(string))
return c
}
// 条件组成
func (c *Clause) addCondition(sql, opt string) {
if c.condition == "" {
c.condition = sql
} else {
c.condition = fmt.Sprint("(", c.condition, ") ", opt, " (", sql, ")")
}
}
// 通过关键字构建sql语句
func (c *Clause) Set(name Type, param ...interface{}) {
sql, vars := generators[name](param...)
c.sqlType[name] = sql
c.paramsType[name] = vars
}
// 拼接各个SQL语句
func (c *Clause) Build(orders ...Type) {
var sqls []string
var vars []interface{}
for _, order := range orders {
if sql, ok := c.sqlType[order]; ok {
sqls = append(sqls, sql)
vars = append(vars, c.paramsType[order]...)
}
}
c.sql = strings.Join(sqls, " ")
c.params = vars
}
```
代码说明:
- 第7~16行:定义sql常量关键词。
- 第19~22行: SetTableName() 方法定义表名。
- 第60~84行:查询条件组装。
- 第86~90行: Set() 方法根据 Type 调用对应的 generator ,生成该子句对应的 SQL 语句。
- 第92~103行: Build() 方法根据传入的 Type 的顺序,构造出最终的 SQL 语句。
# 测试
```go
// clause_test.go
package session
import (
log "github.com/sirupsen/logrus"
"testing"
)
type Users struct {
Name string `torm:"user_name,varchar"`
Age int `torm:"age,int"`
}
func TestClause_InsertStruct(t *testing.T) {
user := &Users{
Name: "迈莫coding",
Age: 1,
}
clause := newClause()
clause = clause.SetTableName("memo").
insertStruct(user)
log.Info(clause.sql)
log.Info(clause.params)
// sql := "INSERT INTO memo (Name,Age) VALUES (?,?)"
}
func TestClause_Condition(t *testing.T) {
clause := newClause()
clause = clause.SetTableName("memo").
andEqual("name", "迈莫coding").
orEqual("age", 5).
selectField("name,age")
log.Info(clause.condition)
log.Info(clause.params)
log.Info(clause.cselect)
}
func TestClause_UpdateStruct(t *testing.T) {
user := &Users{
Name: "迈莫coding",
}
clause := newClause()
clause = clause.SetTableName("memo").
updateStruct(user)
log.Info(clause.sqlType[Update])
log.Info(clause.paramsType[Update])
}
```
结果
```go
=== RUN TestClause_UpdateStruct
time="2021-01-16T10:38:23+08:00" level=info msg="UPDATE memo SET user_name=?,age=?"
time="2021-01-16T10:38:23+08:00" level=info msg="[迈莫coding 0]"
--- PASS: TestClause_UpdateStruct (0.00s)
PASS
```
# 代码目录
```go
torm
|--raw.go // 底层与数据库交互语句
|--raw_test.go
|--schema.go // 对象表结构映射
|--schema_test.go
|--generators.go // 关键词sql语句
|--clause.go // 条件组件库
|--clause_test.go
|--go.mod
```
到这里,第三天的任务也编写完成了,回顾一下,今天主要完成了构建关键词sql语句和条件组件,在 generators 类中完成各个关键词sql语句的编写,在 clause 类中完成条件组件的编写和构建完整sql语句。
> 文章也会持续更新,可以微信搜索「 迈莫coding 」第一时间阅读,回复『1024』领取学习go资料。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210124205045284.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxMDY2MDY2,size_16,color_FFFFFF,t_70#pic_center)
有疑问加站长微信联系(非本文作者))