【七天从零实现ORM|Day04:条件组件库】学完迈入腾讯阿里大厂

38 篇文章 0 订阅
16 篇文章 0 订阅

在这里插入图片描述

今天是【7天从零实现TORM框架】的第三天,主要任务是:

  • 利用 generators 类构建关键词SQL语句,代码100行左右。
  • 利用 Clause 类构建条件组件,120行左右。

若对Go中反射的使用不了解的话,我写了三篇关于反射的文章,给小伙伴提供参考,足以应对本项目中所使用的反射知识点。

go反射第一弹:https://mp.weixin.qq.com/s/F8yZyqC5UwoewsX0THqy1w
go反射第二弹:https://mp.weixin.qq.com/s/lgZykTL8ls6aG0OMNSbZMw
go反射第三弹:https://mp.weixin.qq.com/s/vFt06c9herwTrx1LTxNaKg

源代码:在【迈莫coding】中回复关键字「 torm 」获取github地址链接
后续会为【七天从零实现TORM框架】录制视频,文章+视频+代码

generators构建关键词SQL语句

一条完整的sql语句是由多条子sql语句组装而成的,比如新增sql数据,格式如下所示:

INSERT INTO user(user_name, age) VALUES("迈莫coding", 1);

将一条新增sql语句进行拆分的话,它分为两部分,一部分为 INSERT 关键词sql语句,另一部分是 VALUES 关键词sql语句。这样就把一条完整SQL语句进行拆分存储,接下来就会讲解这一步代码实现思路。

该部分代码存储在根目录下的 generators.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语句等等,结构如下所示:

// 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语句拼接。

// 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 语句。

测试

// 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])
}

结果

=== 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

代码目录

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资料。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值