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方法主要的执行流程就是这样,还有些详细的后续再补充,写的不对的希望给指出更正
有疑问加站长微信联系(非本文作者)