2019-09-09, beego代码走读,五、ORM

aside section ._1OhGeD · · 640 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

先贴一段README中的使用示例,很像JAVA的JPA的操作,算是致敬了吧?。

func init() {
    // register model
    orm.RegisterModel(new(User))

    // set default database
    orm.RegisterDataBase("default", "mysql", "root:root@/my_db?charset=utf8", 30)
    
    // create table
    orm.RunSyncdb("default", false, true)   
}

func main() {
    o := orm.NewOrm()

    user := User{Name: "slene"}

    // insert
    id, err := o.Insert(&user)

    // update
    user.Name = "astaxie"
    num, err := o.Update(&user)

    // read one
    u := User{Id: user.Id}
    err = o.Read(&u)

    // delete
    num, err = o.Delete(&u) 
}

先看一下Init中的方法RegisterModelRegisterDataBaseRunSyncdb

RegisterModel

func registerModel(PrefixOrSuffix string, model interface{}, isPrefix bool) {
    //反射获得类型,通过这两步操作,就可以获得了model interface{}实际上的类型。
    val := reflect.ValueOf(model)
    typ := reflect.Indirect(val).Type()
    //省略。。。
}

通过反射获取到interface{}的类型之后,可以通过类型获得Table的名称。
首先,尝试通过TableName方法获取;如果失败,则使用Type().Name()作为名称。

func getTableName(val reflect.Value) string {
    if fun := val.MethodByName("TableName"); fun.IsValid() {
        vals := fun.Call([]reflect.Value{})
        // has return and the first val is string
        if len(vals) > 0 && vals[0].Kind() == reflect.String {
            return vals[0].String()
        }
    }
    return snakeString(reflect.Indirect(val).Type().Name())
}

然后再调用newModelInfo, 调用addModelFields,通过反射获取到model所有的Fields映射为数据库的Column(画外音:如果有类似Java的Annotation,就会方便很多了)。

最后,将生成的Model放入Cache

modelCache.set(table, mi)

RegisterDataBase

Q: DataBase还是Database,哪个才是正确的名字呢?(*——*)

db, err = sql.Open(driverName, dataSource)

和JDBC的操作很类似,通过driverName打开数据库连接,类似于SPI的操作。

题外话
目前golang直连的数据库驱动很是贫乏,如果做企业级应用的话,还是用Java吧。
avelino/awesome-go 中列举的很多的数据库驱动。例如,Oracle(OCI)这个很复杂。

将数据库连接池进行缓存,将sql/DB对象缓存到dataBaseCache对象中

 addAliasWthDB(aliasName, driverName, db)

检测数据库的时区设置,用于后续的Model中的timestamp类型保存和读取。

detectTZ(al)

设置连接数;我看了下golang SDK中sql/DB支持的设置,还支持超时时间。

SetMaxIdleConns(al.Name, v)
SetMaxOpenConns(al.Name, v)
sql/DB

满足了最基本的数据库连接池的基本素质。

做为比对,感受一下Java生态中的当红辣子鸡Hikari的连接池设置,满满一屏幕的set方法。

Hikari

RunSyncdb

这个没什么特别需要说的内容,就是fmt.Sprintf拼接SQL字符串。

接下来,看一下main中的几个操作,NewOrm,Insert,Update,Read,Delete

NewOrm

NewOrm会首次调用Bootstrap来初始化Model的关联关系,包括OneToOne、OneToMany等等。

const (
    RelForeignKey
    RelOneToOne
    RelManyToMany
    RelReverseOne
    RelReverseMany
)

Bootstrap方法大篇幅的代码都是在构建Model的关系信息。参考下图:
代码繁琐,但是很容易理解,就不逐行分析了。

orm/models_boot.go:93

Bootstrap执行完毕之后,会调用一个Using方法,Using("default")Using方法的核心是从缓存中找到连接池。

if al, ok := dataBaseCache.get(name); ok {
}

dataBaseCache在RegisterDataBase方法中进行了初始化。


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:aside section ._1OhGeD

查看原文:2019-09-09, beego代码走读,五、ORM

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

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