【七天从零实现ORM】Day04:条件组件库

memo012 · · 397 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

![在这里插入图片描述](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)

有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

397 次点击  ∙  1 赞  
加入收藏 微博
上一篇:Go error实践
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传