grom源码分析

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

gorm是用golang写的数据库orm库,目前golang写的orm库也有很多,例如xorm,beego orm,gomybatis等,各有各的优势特点,看一下gorm对golang基础框架中数据库相关接口是如何封装的。

gorm一般的初始化方式

db,err:=gorm.Open("mysql","user:password@/dbname?charset=utf8&parseTime=True&loc=Local")

iferr!=nil{

    log.Errorf("init error!")

}

gorm中DB结构体的定义:

// DB的结构体

typeDBstruct{

    sync.RWMutex// 锁

    Valueinterface{}// 一般传入实际操作的表所对应的结构体

    Errorerror// DB操作失败的error

    RowsAffectedint64// 操作影响的行数

    // single db

    dbSQLCommon// SQL接口,包括(Exec、Prepare、Query、QueryRow)

    blockGlobalUpdatebool// 为true时,可以在update在没有where条件是报错,避免全局更新

    logModelogModeValue// 日志模式,gorm提供了三种  

    loggerlogger// 内部日志实例

    search*search// 查询相关的条件

    valuessync.Map// value Map

    // global db

    parent*DB// 父db,为了保存一个空的初始化后的db,也为了保存curd注册的的callback方法

    callbacks*Callback// callback方法

    dialectDialect// 不同类型数据库对应的不同实现的相同接口

    singularTablebool// 表名是否为复数形式,true时为user,false时为users

}

gorm的Open方法:

funcOpen(dialectstring,args...interface{}) (db*DB,errerror) {

    iflen(args)==0{

        err=errors.New("invalid database source")

        returnnil,err

    }

    varsourcestring

    vardbSQLSQLCommon

    varownDbSQLbool

    switchvalue:=args[0].(type) {

    casestring:

        vardriver=dialect

        iflen(args)==1{

            source=value

        }elseiflen(args)>=2{

            driver=value

            source=args[1].(string)

        }

        // 调用go基础库的Open方法获得db的connention附给dbSQL,

        // 此时还没有真正连接数据库

        dbSQL,err=sql.Open(driver,source)

        ownDbSQL=true

    caseSQLCommon:

        dbSQL=value

        ownDbSQL=false

    default:

        returnnil,fmt.Errorf("invalid database source: %v is not a valid type",value)

    }

    // 初始化DB

    db=&DB{

        db:dbSQL,

        logger:defaultLogger,

        callbacks:DefaultCallback,

        dialect:newDialect(dialect,dbSQL),

    }

    // 将初始化的DB保存到db.parent中

    db.parent=db

    iferr!=nil{

        return

    }

    // 调用go基础库的Ping方法检测数据库connention是否可以连通

    ifd,ok:=dbSQL.(*sql.DB);ok{

        iferr=d.Ping();err!=nil&&ownDbSQL{

            d.Close()

        }

    }

    return

}

gorm是通过多个callbsck方法来实现curd的,具体流程以一个查询为例:

DBEngine.Table(entry.TableName).

Select(entry.Select).

Where(entry.sql,entry.values).

Order(entry.order).

Find(entry.result)

执行步骤:

1.执行Table方法,添加tablename条件:

func(s*DB)Table(namestring)*DB{

    clone:=s.clone()// 执行clone方法也就是从新的db中赋值一个空的,避免交叉影响

    clone.search.Table(name)// 赋值table name

    clone.Value=nil// 附空

    returnclone

}

2.执行Where方法,添加where条件:

// 首先也是调用clone方法,然后调用search的Where方法

func(s*DB)Where(queryinterface{},args...interface{})*DB{

    returns.clone().search.Where(query,args...).db

}

// search的Where方法是将传进来的条件进行拼接,存入search.whereConditions

func(s*search)Where(queryinterface{},values...interface{})*search{

    s.whereConditions=append(s.whereConditions,map[string]interface{}{"query":query,"args":values})

    returns

}

3.执行Order方法,添加order条件:

// 类似Where,reorder为true会强制刷掉gorm默认的order by

func(s*DB)Order(valueinterface{},reorder...bool)*DB{

    returns.clone().search.Order(value,reorder...).db

}

func(s*search)Order(valueinterface{},reorder...bool)*search{

    // 如果为true,先清除s.orders

iflen(reorder)>0&&reorder[0] {

        s.orders=[]interface{}{}

    }

// 将value拼接,存入s.orders

    ifvalue!=nil&&value!=""{

        s.orders=append(s.orders,value)

    }

    returns

}

4.执行Find方法,真正实现查询:

// 首先先创建一个scope(可以理解成只针对本次数据库操作有效的一个环境),再调用inlineCondition内部方法,最后执行callcallbacks一系列方法实现真正的查询操作,并将db返回

func(s*DB)Find(outinterface{},where...interface{})*DB{

    returns.NewScope(out).inlineCondition(where...).callCallbacks(s.parent.callbacks.queries).db

}

// NewScope方法就是初始化一个scope

func(s*DB)NewScope(valueinterface{})*Scope{

    dbClone:=s.clone()

// 此时赋值value

    dbClone.Value=value

    scope:=&Scope{db:dbClone,Value:value}

    ifs.search!=nil{

        scope.Search=s.search.clone()

    }else{

        scope.Search=&search{}

    }

    returnscope

}

// inlineCondition方法是执行scope.Search.Where

func(scope*Scope)inlineCondition(values...interface{})*Scope{

    iflen(values)>0{

        scope.Search.Where(values[0],values[1:]...)

    }

    returnscope

}

// scope.Search.Where实际上也是执行条件拼接,由于我们在调用的时候没有在Find中传入条件,所以这个方法不会被执行

func(s*search)Where(queryinterface{},values...interface{})*search{

    s.whereConditions=append(s.whereConditions,map[string]interface{}{"query":query,"args":values})

    returns

}

// 最重要的就是callcallbacks方法,是真正执行的地方

func(scope*Scope)callCallbacks(funcs[]*func(s*Scope))*Scope{

    deferfunc() {

        iferr:=recover();err!=nil{

            ifdb,ok:=scope.db.db.(sqlTx);ok{

                db.Rollback()

            }

            panic(err)

        }

    }()

// 循环里面所有的注册的funcs

    for_,f:=rangefuncs{

        (*f)(scope)

        ifscope.skipLeft{

            break

        }

    }

    returnscope

}

// 这里的funcs实在程序启动时init方法注册的

funcinit() {

    DefaultCallback.Query().Register("gorm:query",queryCallback)

    DefaultCallback.Query().Register("gorm:preload",preloadCallback)

    DefaultCallback.Query().Register("gorm:after_query",afterQueryCallback)

}

// 比如afterQueryCallback方法还提供了反射调用结构体的AfterFind方法,如果在查询前结构体实现了AfterFind方法就会被调用,这个机制比了灵活

funcafterQueryCallback(scope*Scope) {

    if!scope.HasError() {

        scope.CallMethod("AfterFind")

    }

}

Find方法主要的执行流程就是这样,还有些详细的后续再补充,写的不对的希望给指出更正


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

本文来自:简书

感谢作者:一只努力的微服务

查看原文:grom源码分析

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

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