xorm一次get请求

xorm的一次get请求做了什么?

func TestUser_Info(t *testing.T) {
	user := &model.User{}
	//var form *model.User
	println("BEFORE", unsafe.Pointer(user), unsafe.Pointer(&user.NickName))
	sql.Session().ID(1).Get(user)
	println("AFTER", unsafe.Pointer(user), unsafe.Pointer(&user.NickName))

}

xorm一次get请求_第1张图片
运行测试方法,跟着断点调试下去
xorm一次get请求_第2张图片
xorm中对get方法的描述是:从数据库中检索一条记录,检索的过程中会把非空字段当做条件。xorm认定非空字段条件:number != 0,string != “”,slice != nil,如果是自动关闭的回话,xorm会自动关闭它(默认为自动关闭的回话,所以在业务代码中基本不需要defer session.Close())
xorm一次get请求_第3张图片
如果bean不是指针或者bean是指针同时bean的value也是指针,报错。这两种情况是大部分goalng 新手容易掉进去的坑,框架首先判断了这两种情况,通过值传递做的更改不算数。
xorm一次get请求_第4张图片
这里调用了setRefBean 设置bean的引用,就是根据bean初始化一些参数。
xorm一次get请求_第5张图片
方法调用了rValue去获得real value ,用了golang的反射,rValue调用了ValueOf,ValueOf函数返回一个Value类型值,该值代表运行时的数据。
xorm一次get请求_第6张图片
调用ValueOf会发生指针逃逸,指针会从栈上逃到堆上 escapes(i)
*返回unpackEface(i),unpackEface会把空指针搞成一个实际对象(如果i为空的话),所以&User{},var user User没区别。
xorm一次get请求_第7张图片
*TIPS:|= 是位运算符表示按位或后赋值。
xorm一次get请求_第8张图片
得到实际值后,xorm调用autoMapType自动映射类型信息,这个函数有锁。首先尝试在engine.Tables中寻找t类型代表的表名字,如果有,使用,如果没有,自己映射一个,算个缓存吧。现在是第一次访问,进入if判断。
xorm一次get请求_第9张图片
这个函数的目的是把下图的table信息获取出来。
xorm一次get请求_第10张图片
xorm一次get请求_第11张图片
调用tbNameForMap(v)
xorm一次get请求_第12张图片
Implements会报告这个struct是否实现了TableName接口。如果实现了调用返回,这就是为什么Struct implement TableName 能够自定义表名。(下面代码顺序是错的只供阅读)


tpTableName = reflect.TypeOf((*TableName)(nil)).Elem()

// TableName table name interface to define customerize table name
type TableName interface {
	TableName() string
}

return v.Interface().(TableName).TableName()

如果没有实现,调用xorm中的Obj2Table,这又是另一组接口,可以自定义实现,随便怎么搞。
xorm一次get请求_第13张图片
默认实现也有很多,能满足大部分需求。
xorm一次get请求_第14张图片
xorm一次get请求_第15张图片
跑完tbNameForMap(v),知道我们的表名了t_user,开始处理列,接下来是对某一列的处理。最终的目的是为了构造
xorm一次get请求_第16张图片
这个结构体,首先看看这个属性有没有xorm这个tag 在这里插入图片描述
如果xorm不为空,先去处理tags
xorm一次get请求_第17张图片
从代码来不同tag之间用空格分割。
在这里插入图片描述
循环对tags进行处理,如果当前的tag是-,忽略。现在我们的tags不是空 ,进入if,申明了一个tag context.
这里首先对xorm:"extends"进行了处理。
xorm一次get请求_第18张图片
如果现在的字段是extends并且是一个结构体,那么递归调用mapType,如果当前的extends是一个指针,那么xorm会先把指针搞成struct,然后在进行mapType,这里有一个不大常用的golang 的关键字,fallthrouth,意思是继续执行下一个case而不去校验下一个条件是否符合。

xorm一次get请求_第19张图片
继续向下会发现一个xorm的tagHandles,里面存放了每一种tag怎么处理。下图是默认的tag处理方式。
xorm一次get请求_第20张图片

名称 作用
<- 只从db中取出来,不放进去
-> 和上面相反
PK primary key主键约束
NULL 列可以为空
NOT NOT
AUTOINCR 自增
DEFAULT 默认值
CREATED 标记创建时间
UPDATED 和上个功能类似
DELETED 和上个功能类似,时间不为空默认表示被删除了
version 乐观锁
UTC 时区处理(没用过)
LOCAL 本地时区
NOTNULL 不能为空
INDEX 是否是索引
UNIQUE 是否唯一
CACHE 当前字段缓存
NOCACHE 不缓存
COMMENT 注释

这里普及一下乐观锁和悲观锁
悲观锁,正如其名,它指的是对数据被外界(包括当前系统的其它事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排它性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。
乐观锁认为一般情况下数据不会造成冲突,所以在数据进行提交更新时才会对数据的冲突与否进行检测。如果没有冲突那就OK;如果出现冲突了,则返回错误信息并让用户决定如何去做。乐观锁的特点是先进行业务操作,不到万不得已不会去拿锁。乐观地认为拿锁多半会是成功的,因此在完成业务操作需要实际更新数据的最后一步再去拿一下锁。
乐观锁可以代码实现。
继续进行,下一步会判断sql type,也就是golang 的数据类型和sql之间的数据类型的转换。
在这里插入图片描述
xorm一次get请求_第21张图片
上面图处理了所有的类型映射。下面是所有能够处理的数据库类型

Bit       = "BIT"
	TinyInt   = "TINYINT"
	SmallInt  = "SMALLINT"
	MediumInt = "MEDIUMINT"
	Int       = "INT"
	Integer   = "INTEGER"
	BigInt    = "BIGINT"

	Enum = "ENUM"
	Set  = "SET"

	Char             = "CHAR"
	Varchar          = "VARCHAR"
	NVarchar         = "NVARCHAR"
	TinyText         = "TINYTEXT"
	Text             = "TEXT"
	NText            = "NTEXT"
	Clob             = "CLOB"
	MediumText       = "MEDIUMTEXT"
	LongText         = "LONGTEXT"
	Uuid             = "UUID"
	UniqueIdentifier = "UNIQUEIDENTIFIER"
	SysName          = "SYSNAME"

	Date       = "DATE"
	DateTime   = "DATETIME"
	Time       = "TIME"
	TimeStamp  = "TIMESTAMP"
	TimeStampz = "TIMESTAMPZ"

	Decimal = "DECIMAL"
	Numeric = "NUMERIC"

	Real   = "REAL"
	Float  = "FLOAT"
	Double = "DOUBLE"

	Binary     = "BINARY"
	VarBinary  = "VARBINARY"
	TinyBlob   = "TINYBLOB"
	Blob       = "BLOB"
	MediumBlob = "MEDIUMBLOB"
	LongBlob   = "LONGBLOB"
	Bytea      = "BYTEA"

	Bool    = "BOOL"
	Boolean = "BOOLEAN"

	Serial    = "SERIAL"
	BigSerial = "BIGSERIAL"

	Json  = "JSON"
	Jsonb = "JSONB"
golang sql
int,int8,int16,int32,uint,uint8,uint32 int
int64,uint64 bigint
float32 float
float64 double
Complex64(复数),Complex128 varchar
array,slice,map 如果内容是byte用blob,其他的用text
bool bool
string varchar
struct 结构体的处理是如果能转换成time.Time 就是time.Time如果不是,就是text
指针 递归调用Type2SQLType()方法
如果这些都没有命中,那么默认的sql type 是text

xorm一次get请求_第22张图片
类型映射完成之后,接下来映射列名,和表名类似,用的是一套接口。
xorm一次get请求_第23张图片
上图是这个方法中对tag的处理,如果不存在tags 会进入到else中。xorm一次get请求_第24张图片
上图所示,else中有个关键的地方是,xorm首先判断当前的field是否是可以寻址的,可寻址的数据类型有

an element of a slice
an element of an addressable array
a field of an addressable struct
the result of dereferencing a pointer.

在这里插入图片描述
上图是个有趣的写法,把Interface转换为core.Conversion来判断当前的结构是否实现了Conversion接口。
xorm一次get请求_第25张图片
如果实现了Conversion接口就用实现的去作,如果没有实现就用默认的type2SQLType方法去作转化。这个地方变量命名好像有点问题,把ok改成notok更直观。一次循环结束的结果是构造了一个col对象,这时候table.AddColumn(col)
xorm一次get请求_第26张图片
xorm一次get请求_第27张图片
上图所示,这里对id有个特殊处理,就是说如果你的代码里没有特殊表示主键字段,那么ID就是主键。
xorm一次get请求_第28张图片
随后处理缓存,顺便一提,xorm中使用的缓存算法是LRU算法,使用方法也很简单,API设置一下就行了

cacher := xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)
x.SetDefaultCacher(cacher)

当然你也可以换成自己的缓存算法。
在这里插入图片描述

这个时候mapType方法跑完了
xorm一次get请求_第29张图片
EXTENDS也跑完了。
xorm一次get请求_第30张图片
setRefBean也跑完了,这时候所有的信息已经准备好了,接下来就是连接数据库进行查询了。
查询之前好像还缺少数据库语句,那么接下来要作的事情就是根据之前得到的信息,构造出数据库语句。
xorm一次get请求_第31张图片
首先判断一下是不是手写的语句,如果是手写的语句,就不要去genGetSql了,我们现在不是那么进入这个if判断。这时候我们运行的是get方法所以api偷偷给我们加上了一个Limit(1)
xorm一次get请求_第32张图片
上图的方法是把之前生成的列信息搞成sql列,搞列的过程中,有些tags就起作用了如果是OnlyToDB的,就不会在这个查询中出现这个列。其中也有对表别名的处理。
xorm一次get请求_第33张图片
有了列之后开始去添加查询条件,就像一开始所说的,如果struct中的属性不是zero那么会默认当成一个查询条件。
xorm一次get请求_第34张图片
xorm一次get请求_第35张图片
这个方法先判断是不是调用了noAutoCondition,如果是就不拼接自动生成的条件,如果不是去buildConds,
buildConds方法有点长,只要记住它作的事情就是把struct中不是zero的值拿出来按照规范拼接成查询条件就行了。
statement.cond = statement.cond.And(autoCond)
这里statement.cond对象是另外一个包的对象,xorm一次get请求_第36张图片
这个包可以用golang 代码来写sql.
在这里插入图片描述
最后单独处理一下主键。
xorm一次get请求_第37张图片
这里的buider对象是另外一个包的内容,这里不详细展开。
xorm一次get请求_第38张图片
经过一系列转换之后,我们的sql语句拼接完成了,这时候可以去访问数据库了。xorm一次get请求_第39张图片
访问之前看看是不是开启了缓存,如果开启了缓存,现在缓存中找找看。
在这里插入图片描述
如果这个缓存中能够找到记录,xorm会打印日志,平时我没有看到日志,也没有设置过缓存,所以缓存默认是关闭的状态。
在这里插入图片描述
接下来进入数据库访问的过程。
首先调用
xorm一次get请求_第40张图片
最后调用
在这里插入图片描述
所以真正做事情的对象是DB这个对象,DB这个对象组合了,sql.DB底层还是用的golang 自己的sql包
xorm一次get请求_第41张图片
xorm一次get请求_第42张图片
上图所示这个时候已经查询完成了,现在就是把查询得到的值在赋值给bean,方法大致和之前类似。
xorm一次get请求_第43张图片
最后查询之后放入缓存,至于用不用缓存就看用户设置了。
以上就是xorm中一次get的整个流程。

你可能感兴趣的:(golang,orm)