分享
  1. 首页
  2. 文章

grom源码分析

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

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

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

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏