一个基于Go 语言的数据库 ORM 设计史

这个库源自公司游戏服务器框架。当初第一版设计时,golang发型版本为1.1 , 市面上没有太多下成的orm . 我们用python搭建的webapi ,golang直接通过webapi操作数据库, 适用一段时间。当时负责写api 的绕了不少远路, 为每个module 独立一个操作借口,这样的结果是webapi部分一对的工作,golang部分又一堆的工作。一个地方变动,一堆变动。

后来,更新的第二版, 一个固定的webapi,用_db=DATABASENAME&_tb=TABLENAME&field=_xxx 这种方式实现数据库查询, 然后golang部分也实现了通过一个 struct 制定tag 的方式, 动态调用这个api。 到这一步, orm 其实已经很好用了, 专心设计数据库, 和专心写struct 就可以。 但问题出来了一些, api部分功能升级,牵扯到了golang库的变动, 就算尽量保持一致, 累的很难维护, 新项目时, 可能又要独立一套webapi(项目服务器完全分开)。当时设计的struct 操作方法, 也有些麻烦,比如(老方法, 于公开的版本无关):

//修改一个累的方法。
type UserInfo struct {
Uid uint32 `json:"uid" index:"pk"`
UserName string `json:"username" `
Money int `json:"money"`

u:=new(UserInfo)
u.Uid=1
mode:=orm.PlotCache(u)
mode.Set("UserName" , "xxxx")
mode.Save() 
mode.Incy("money",+1)
mode.Save()

u = new(UserInfo)
u.Uid=0
u.UserName="xxxx"
u.Money =111
orm.Save(u) //添加

u = new(UserInfo)
u.Uid=1
u.UserName="xxxx"
u.Money =111
orm.Save(u) //修改

看似还可以。 不过每次使用时, 你的代码就必须有一个mode 的变量在跟着转悠, orm变量来回调用, 代码量大了, 会让阅读很困难。 于是, 有了现在的发布的这一版ORM .

type UserInfo struct {
orm.Object
Uid uint32 `field:"uid" index:"pk"`
UserName string `field:"string"`
Money int `json:"money"`
}
func(self *UserInfo)GetTableName()string{
return "database.tablename"
}


u:=new(UserInfo)
all,err:= u.Objects(u).All()  //提取所有

all,err:= u.Objects(u).Filter("Uid__gt" , 100).All()   //  Uid  > 100 的。 

u.Objects(u).Filter("Uid__gt",100).Limit(0,10).All() //取10条记录。 

param:= u.Objects(u).Filter("Uid__gt",100)
param.FilterOr("Money__lt", 500)
param.All() // Uid >100 or Money < 500的。 


u:=new(UserInfo)
u.Uid=1
u.Objects(u).One() //提取一个

u.UserName="111111"
u.Money = 1000
u.Save() //保存
u.Change("Money__add" , 1) //money +1 
u.Save() //保存。 

u.Delete() //删除掉


这样操作起来容易很多了。 还有更多的操作符, 可以看 https://github.com/ablegao/orm/blob/master/marsharl_driver_mysql.go 里面_w 函数方法

这个版本的orm , 直接使用的sql 驱动, 不在受webapi的限制。 而扩展数据库很容易, 可以参考marsharl_driver_mysql.go 里面的内容。

上面这些,并非这个orm的重点功能。频繁的数据库修改,和一些没必要必须摄入到数据库里面的数据缓存, 是这个ORM的重点。

所以这个ORM有另外一个Redis缓存扩展:

type UserInfo struct {
  orm.CacheModule   //注意这里的变化
  Uid uint32 `field:"uid" index:"pk" cache:"info" cache_profix:"user"`
  UserName string `field:"string"`
  Money int `json:"money"`
  }
  func(self *UserInfo)GetTableName()string{
  return "database.tablename"
  }

和上面的类区变化不大,更换了orm.Object 为orm.CacheModule . 增加cache tag , 和第一个字段增加 cache_profix.

很抱歉的是, 我很难保证orm.CacheModule 和orm.Object的通用性, 所以 如果要切换时, Filter方法有些字段不可用。 

u:=new(UserInfo)
u.Uid=1
u.Objects(u).One()

这样, redis中会增加一个key user:1:info 内容是hash 的userInfo所有字段数据。 

然后你下次在使用时:

u:=new(UserInfo)
u.Uid=1
u.Objects(u).One() //你的数据将会从Redis中提取, 而不是Mysql 


然后修改:

u.Incry("Money" , +1)
u.Save() //同步到数据库。 不执行 只变更redis


u.Set("Money" , "aaaaaa") //修改. 
u.Save() 

这样, 这个orm 算是实现了。 很多细节, 这里不多说。待续。


项目Github地址:http://github.com/ablegao/orm

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。