使用 Go 打造另一款简单实用的 ORM( 四 )

我们再convert.go里增加extractTagInfo的实现
//提取tag信息func extractTagInfo(st reflect.Value) (tagList map[string]reflect.Value, err error) { stVal := reflect.Indirect(st) if stVal.Kind() != reflect.Struct { return nil, fmt.Errorf("the variable type is %v, not a struct", stVal.Kind()) } tagList = make(map[string]reflect.Value) for i := 0; i < stVal.NumField(); i++ { //获取结构体成员 v := stVal.Field(i) if v.Kind() == reflect.Ptr { //如果没有初始化,则需要先初始化 if v.IsNil() { var typ reflect.Type if v.Type().Kind() == reflect.Ptr { typ = v.Type().Elem() } else { typ = v.Type() } vv := reflect.New(typ) v.Set(vv) } //如果是结构体指针,则在进行提取 if v.Elem().Kind() == reflect.Struct { t, err := extractTagInfo(v.Elem()) if err != nil { return nil, err } for k, ptr := range t { if _, ok := tagList[k]; ok { return nil, fmt.Errorf("%s:%s is exists", "db", k) } tagList[k] = ptr } } } else if v.Kind() == reflect.Map && v.IsNil() { //如果是map类型,并且没有初始化,则需要初始化一下 v.Set(reflect.MakeMap(v.Type())) } else if v.Kind() == reflect.Struct { var ignore bool //以下的类型,会再scan的执行转换,所以不需要二次处理 switch v.Interface().(type) { case time.Time: ignore = true case sql.NullTime: ignore = true case sql.NullString: ignore = true case sql.NullBool: ignore = true case sql.NullInt64: ignore = true case sql.NullInt32: ignore = true case sql.NullFloat64: ignore = true } if !ignore { t, err := extractTagInfo(v) if err != nil { return nil, err } for k, ptr := range t { if _, ok := tagList[k]; ok { return nil, fmt.Errorf("%s:%s is exists", "db", k) } tagList[k] = ptr } } } tagName := stVal.Type().Field(i).Tag.Get("db") if tagName != "" { //tag内容通过";"进行分割 attr := strings.Split(tagName, ";") column := attr[0] if _, ok := tagList[column]; ok { return nil, fmt.Errorf("%s:%s is exists", "db", tagName) } //字段对应结构体成员地址 tagList[column] = v } } return}至此,我们支持了ToArray(),ToMap(),ToStruct(),我们还支持结构体以下方式
type user struct { Id int `db:"id"` Name string `db:"name"` Area areaInfo}type areaInfo struct { province string `db:"province"` city string `db:"city"`}如果你不需要链式操作,那么到这里就已经够了 。
第四步: 打造SQL构造器和解析器
我们先创建builder.go,grammar.go用于实现构造器和解析器 builder.go
type Builder struct { table string conn *Connection grammar *Grammar}func newBuilder(conn *Connection, grammar *Grammar) *Builder { b := new(Builder) b.conn = conn b.grammar = grammar return b}func (b *Builder) Table(table string) *Builder { b.table = table return b}grammar.go
type Grammar struct {}func NewGrammar() *Grammar { return new(Grammar)}链式操作的起始都是以Table开始,所以我们再connection.go和kdb.go增加Table的操作 connection.go
func (c *Connection) Table(table string) *Builder { return c.query().Table(table)}func (c *Connection) query() *Builder { g := NewGrammar() b := newBuilder(c, g) return b}kdb.go
func Table(table string) *Builder { return newConnection().Table(table)}Ok,现在我们可以通过kdb.Table("user")进行操作了 。在实现Builder和Grammar之前,我们先来分析一下简单的SQL语句
Select * From User Where id = 1;我们可以把上面的语句拆分成
1.SQL类型:Select/Insert/Update/Delete 2.字段:"*"3.表:"FROM User" 4.条件:"Where id = 1"
所以需要在builder.go中增加Select/Insert/Update/Delete的方法,并且需要存储table,字段,和where条件,我们原生的SQL是采用参数绑定的方式,所以我们还需要增加 addBindings和getBingding是方法 builder.go的实现
type Builder struct { table string conn *Connection grammar *Grammar bindings map[string][]interface{} columns []string wheres []where}type where struct { typ string //类型,用来表示where的种类 column interface{} //字段 operator string //操作符 value interface{} //值 glue string //连接符}func newBuilder(conn *Connection, grammar *Grammar) *Builder { b := new(Builder) b.conn = conn b.grammar = grammar b.bindings = make(map[string][]interface{}) return b}func (b *Builder) Table(table string) *Builder { b.table = table return b}func (b *Builder) Select(columns ...string) *Builder { if len(columns) == 0 { columns = append(columns, "*") } b.columns = columns return b}func (b *Builder) Where(column interface{}, args ...interface{}) *Builder { if len(args) == 0 { return b.WhereIsNull(column) } w := new(where) w.column = column w.glue = "and" w.typ = "basic" switch len(args) { case 1: w.operator = "=" w.value = https://www.isolves.com/it/cxkf/yy/go/2021-03-03/args[0] case 2: w.operator = args[0].(string) w.value = args[1] case 3: w.operator = args[0].(string) w.value = args[1] w.glue = args[2].(string) case 4: w.operator = args[0].(string) w.value = args[1] w.glue = args[2].(string) w.typ = args[3].(string) } b.addBinding("where", []interface{}{w.value}) b.wheres = append(b.wheres, *w) return b}func (b *Builder) WhereIsNull(column interface{}) *Builder { w := new(where) w.column = column w.glue = "and" w.typ = "null" w.operator = "is" w.value = "null" b.wheres = append(b.wheres, *w) return b}func (b *Builder) Get(columns ...string) *Rows { if len(columns) > 0 { b.Select(columns...) } return b.runSelect()}func (b *Builder) addBinding(typ string, value []interface{}) { if _, ok := b.bindings[typ]; ok { b.bindings[typ] = append(b.bindings[typ], value...) } else { b.bindings[typ] = value }}func (b *Builder) getBindings() (bindings []interface{}) { bindings = make([]interface{}, 0) if v, ok := b.bindings["where"]; ok { bindings = append(bindings, v...) } return}func (b *Builder) toSQL() string { return b.grammar.compileSelect(b)}func (b *Builder) runSelect() *Rows { return b.conn.Select(b.toSQL(), b.getBindings())}


推荐阅读