先贴一段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中的方法RegisterModel
,RegisterDataBase
,RunSyncdb
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)
满足了最基本的数据库连接池的基本素质。
做为比对,感受一下Java生态中的当红辣子鸡Hikari
的连接池设置,满满一屏幕的set
方法。
RunSyncdb
这个没什么特别需要说的内容,就是fmt.Sprintf
拼接SQL字符串。
接下来,看一下main
中的几个操作,NewOrm
,Insert
,Update
,Read
,Delete
NewOrm
NewOrm会首次调用Bootstrap
来初始化Model的关联关系,包括OneToOne、OneToMany等等。
const (
RelForeignKey
RelOneToOne
RelManyToMany
RelReverseOne
RelReverseMany
)
Bootstrap方法大篇幅的代码都是在构建Model的关系信息。参考下图:
代码繁琐,但是很容易理解,就不逐行分析了。
Bootstrap执行完毕之后,会调用一个Using方法,Using("default")
,Using
方法的核心是从缓存中找到连接池。
if al, ok := dataBaseCache.get(name); ok {
}
dataBaseCache
在RegisterDataBase方法中进行了初始化。