go语言orm解读(备用)

/*以下是前提知识/

Reflect,也就是反射,golang中的反射机制,就是在运行时动态的调用对象的方法和属性。Golang的指定类型的变量的类型是静态的(int,string…),在创建变量的时候就已经确定,反射主要与golang的interface类型相关,,只有interface类型才有反射一说
interface: go语言的接口,主要通过interface实现多态,泛型编程
interface{} 即可以接收任意类型
每个interface都有一个pair -> 也就是type 和value
一个interface{} 类型的变量包含了2个指针,一个指针指向值的类型,另一个指针指向实际的值。
反射就是用来检测存储在接口变量内部pair对的一种机制
reflect的基本功能Typeof和Valueof
Valueof 用来获取输入参数接口中的数据的值,如果接口为空则返回0
Typeof 用来动态获取输入参数接口中的值的类型,如果接口为空则返回nil
v.Interface() à获得接口变量的真实内容。然后可以通过类型判断进行转换,转换为原有真实类型
如:relValue := value.Interface().(已知类型)
反射可以将『反射类型对象』重新转换为『接口类型变量』
reflect.Typeof.NumField()à类型个数
reflect.Typeof.Field(i) field包含名称以及类型
reflect.Typeof.NumMethod()方法个数,
reflect.Typeof.Method(i) 方法名以及类型
reflect.Value是通过reflect.ValueOf(X)获得的,只有当X是指针的时候,才可以通过reflect.Value修改实际变量X的值,即:要修改反射类型的对象就一定要保证其值是『addressable』的。
结构体经常需要和Tag其他的系统进行数据交互,例如转成json格式,存储到数据库等,这个时候如果用属性名来作为键值可能不一定会符合项目要求。所以就多了小米点的内容,在go中叫做标签Tag,在转换成其他的数据格式的时候,会使用其中特定的字段作为键值,当我们需要自己封装一些操作,需要用到Tag中的内容时,可以使用反射包中的方法来获取:

   t  := reflect.TypeOf(u)
   filed := t.Elem().Field(0)
   fmt.Println(field.Tag.Get(“json”))
   fmt.Println(field.Tag.Get(“name”))

/前提知识完毕/
以下是ORM工作流程:
/以下部分是整个映射以及数据库操作的流程
/
go语言orm解读(备用)_第1张图片
go语言orm解读(备用)_第2张图片
整个流程:
实例化一个数据实体如ui:=UserInfo{1,”limeng”,”test”,”1980-10-10”}
实例化完成后将实体ui的成员与表名形成映射关系;
映射关系如上图
其中看似冗余的映射,均是为了数据库的操作服务的,如实体中的信息,是为了通过表名找回实体,给查询使用
实体字段与表字段的映射在查询返回结果时依然用到
如何形成映射关系:
调用register函数形成映射关系,其中调用getTableInfo(model Interface{})函数去获取实体的表信息à其中model Interface{} à表示接收任意类型的参数,我们将我们实体(结构体)传入后,当使用的时候,需要通过反射机制,来获取实体(结构体)信息{ 反射也就是将interface类型的数据还原其本身的样子 } à我们实例化出实体所使用的结构体如下:
type UserInfo struct{
TableName orm.TableName “userinfo”
UserName string name:"username"
Uid int name:"uid"PK:"true"auto:"true"
DepartName string name:"departname"
Created string name:"created"
}
Go 中结构体允许标签(Tag)的存在,标签的使用不仅可以让结构体联合json,db操作简单,并且可以给每个字段设置属性值;
我们将数据还原之后,通过NumField 与Filed函数,遍历每一个字段,并解析出其中的信息,在我们设计的结构体中,结构体字段的名称与实际表名是相同的,并且将每个字段解析之后,放入上面映射图的表字段信息中,将表名以及实体中的值也放入其中,映射关系中的表信息就填充结束了。
最后register函数将实体信息放入映射图的实体信息中。
接下来,就是对于真正的数据库的操作:
下图是数据库操作所需要的结构体以及依赖关系:
go语言orm解读(备用)_第3张图片
MysqlDB Params ParamField
Attention!!!
其中参数主要是针对查询时所设定的,因为增、删、改操作可以通过getTableInfo获得所需信息,而查询需要指定条件,所以参数是必须的。
打开数据库并将返回的指针赋值给结构体MysqlDB中的sql.DB,通过使用sql.DB就可以实现对数据库的相应操作。
就增、删、改三个操作来说:它们的业务流程相似,所以在这里只拿出增加的函数来讨论:
Insert(model interface{})函数:
首先,它是MysqlDB的方法;
其次,它的参数类型是interface{}所以,他可以接收任意类型的参数。
在我们的操作过程中,传的是实体,也就是我们实例化过的结构体,Insert内部,会调用orm中的generateInsertSql而generateInsertSql又会调用getTableInfo用来获取实体相关的信息以及对应的数据库的信息à接下来,generateInsert会将得到的信息,加上『insert into 』拼接成sql语句中的插入语句,并将实体的值信息作为参数params,最后,将拼接完成的sql语句、参数切片、以及表信息传出。然后Insert函数会使用sql.DB指针调用sql语句执行函数,得到数据库的结果后,再将已经插入的信息中的自增主键uid的值设置回映射中的valve。以方便后续的使用。
查询相对复杂,也是最最频繁的操作,首先通过From,Where,Select函数获取相对应的指令以及参数,在使用getSelectSql函数拼接成select语句,在getSelect中,通过对条件Where的switch操作,以及使用特定的”__” 作为分隔符将条件与后面的gt,lt的情况分割并且生成相应的指令。将得到的参数以及sql语句,拼接后执行,并将结果集放入切片中返回,在return 时 ,使用的 return myrows.To(this.from),涉及到了两个函数,一个Next,一个To。
Next中涉及到的结构体:
type MyRows struct{
* sql.Rows
Values map[string]interface{} //表字段和值的映射(也就是字段名和值一一对应)
ColumnNames []string //表字段名集合
}
Next工作流程:
首先,调用mysql的next函数,将结果集内的一行数据读出,
表字段名集合填充(也就是将各个字段名存入集合)
初始化表字段和值的映射
然后:
以下四步是最重要的四步。
1.scanArgs := make([]interface{}, len(this.ColumnNames)) 准备好Scan函数所需的类型切片。
2.values := make([][]byte, len(this.ColumnNames)) 字段与values[i] 一一对应
3. for i := range values{
scanArgs[i] = &values[i] 逐个赋值到参数(无法直接赋值)
}
4.this.Rows.Scan(scanArgs…)(mysql的函数,扫描每个字段并返回相应的值)到values中
将values存的值放入结构体MyRows的Values中。
Return true
To工作流程:
设置依据表名的实体映射mi
使用getTableInfo函数使用映射中存的实体的信息参数,来得到数据库表的信息
v := reflect.New(reflect.TypeOf(mi.Model).Elem()).Elem()这句话就是将传入的实体类型得出(还原)
遍历MyRows中的Values(也就是每个数据库的信息)
通过表名映射实体字段名,并将数据库查询出的结果,每个字段以其本身的类型和值放入v中,并且最后append(添加至)切片

你可能感兴趣的:(沉淀)