簡介
go(golang)輕量級ORM,零依賴,支持達夢(dm),金倉(kingbase),神通(shentong),南大通用(gbase),mysql,postgresql,oracle,mssql,sqlite數(shù)據(jù)庫.
源碼地址:https://gitee.com/chunanyong/zorm
作者博客:https://www.jiagou.com
實踐項目
夜鶯監(jiān)控告警項目國產(chǎn)化數(shù)據(jù)庫的支持
夜鶯是一套滴滴團隊主導開源的分布式高可用的運維監(jiān)控系統(tǒng)蝗岖。
源碼地址:https://github.com/didi/nightingale
改造方案
夜鶯使用的是xorm作為ORM庫,不支持國產(chǎn)數(shù)據(jù)庫斗锭。嘗試過通過ODBC的方式連接達夢摊册,操作時遇到了語法不兼容問題嗜暴。之后又了解到zorm耸棒,這個本身天然支持國產(chǎn)數(shù)據(jù)庫的ORM框架。
面前出現(xiàn)了兩種選擇: 一是繼續(xù)嘗試ODBC的方式橡淆,一庫一庫的調(diào)試,手動解決各種兼容問題母赵。二是替換項目的orm庫逸爵,改造一次,就可以把國產(chǎn)四庫的支持全部搞定凹嘲。想想還是后者比較香师倔,改造走起!
改造過程
首先數(shù)據(jù)庫初始化周蹭,夜鶯項目有多個數(shù)據(jù)庫
//聲明存儲多庫連接信息的map趋艘,key是dbname疲恢,value是DBDao
var DB = map[string]*zorm.DBDao{}
func InitMySQL(names ...string) {
//讀取mysql.yml數(shù)據(jù)庫配置文件
confdir := path.Join(runner.Cwd, "etc")
mysqlYml := path.Join(confdir, "mysql.local.yml")
if !file.IsExist(mysqlYml) {
mysqlYml = path.Join(confdir, "mysql.yml")
}
confs := make(map[string]MySQLConf)
err := file.ReadYaml(mysqlYml, &confs)
if err != nil {
log.Fatalf("cannot read yml[%s]: %v", mysqlYml, err)
}
//多庫,不同的name
count := len(names)
for i := 0; i < count; i++ {
conf, has := confs[names[i]]
if !has {
log.Fatalf("no such mysql conf: %s", names[i])
}
dbDaoConfig := zorm.DataSourceConfig{
//DSN 數(shù)據(jù)庫的連接字符串
DSN: conf.Addr,
//數(shù)據(jù)庫驅(qū)動名稱:mysql,postgres,oci8,sqlserver,sqlite3,dm,kingbase 和DBType對應(yīng),處理數(shù)據(jù)庫有多個驅(qū)動
DriverName: "kingbase",
//數(shù)據(jù)庫類型(方言判斷依據(jù)):mysql,postgresql,oracle,mssql,sqlite,dm,kingbase 和 DriverName 對應(yīng),處理數(shù)據(jù)庫有多個驅(qū)動
DBType: "kingbase",
//MaxOpenConns 數(shù)據(jù)庫最大連接數(shù) 默認50
MaxOpenConns: 50,
//MaxIdleConns 數(shù)據(jù)庫最大空閑連接數(shù) 默認50
MaxIdleConns: 50,
//ConnMaxLifetimeSecond 連接存活秒時間. 默認600(10分鐘)后連接被銷毀重建.避免數(shù)據(jù)庫主動斷開連接,造成死連接.MySQL默認wait_timeout 28800秒(8小時)
ConnMaxLifetimeSecond: 600,
//PrintSQL 打印SQL.會使用FuncPrintSQL記錄SQL
PrintSQL: conf.Debug,
}
// 根據(jù)dbDaoConfig創(chuàng)建dbDao, 一個數(shù)據(jù)庫只執(zhí)行一次,第一個執(zhí)行的數(shù)據(jù)庫為 defaultDao,后續(xù)zorm.xxx方法,默認使用的就是defaultDao
dbDao, _ := zorm.NewDBDao(&dbDaoConfig)
DB[names[i]] = dbDao
}
}
建立連接
通過傳入不同的dbName 獲取綁定連接的Ctx
func getNewCtx(dbName string) context.Context {
var ctx = context.Background()
var dbDao *zorm.DBDao = DB[dbName]
newCtx, err := dbDao.BindContextDBConnection(ctx)
if err != nil { //標記測試失敗
log.Fatalf("錯誤:%v", err)
}
return newCtx
}
操作
zorm還有一個方便實用的點:
-
自帶代碼生成器
代碼生成器
- 在codeGenerator.go 中配置好數(shù)據(jù)庫的連接信息
- 如果只想初始化單張表致稀,請修改codeGenerator_test.go 中TestCodeGenerator調(diào)用的code參數(shù)冈闭,為表名
- 單表初始化:執(zhí)行g(shù)o test -v codeGenerator_test.go codeGenerator.go -test.run TestCodeGenerator
整個庫初始化:執(zhí)行g(shù)o test -v codeGenerator_test.go codeGenerator.go -test.run TestCodeGeneratorALL
生成器代碼簡單清晰俱尼,可根據(jù)自己的需求做一些調(diào)整抖单,使最終生成的代碼能一步到位。
生成效果
拿一個表舉例
//table name
const NodeStructTableName = "node"
// NodeStruct
type Node struct {
zorm.EntityStruct
Id int64 `column:"id" json:"id"`
Pid int64 `column:"pid" json:"pid"`
Name string `column:"name" json:"name"`
Path string `column:"path" json:"path"`
Leaf int `column:"leaf" json:"leaf"`
Note string `column:"note" json:"note"`
}
func (entity *Node) GetTableName() string {
return NodeStructTableName
}
//GetPKColumnName 獲取數(shù)據(jù)庫表的主鍵字段名稱.因為要兼容Map,只能是數(shù)據(jù)庫的字段名稱.
func (entity *Node) GetPKColumnName() string {
return "id"
}
增刪改查
使用原生的sql語句,沒有對sql語法做限制.語句使用Finder作為載體
//查詢
var obj Node
//has, err := DB["mon"].Where(col+"=?", val).Get(&obj) 這是之前xorm的代碼
//獲取對應(yīng)數(shù)據(jù)庫的連接
newCtx := getNewCtx("mon")
finder := zorm.NewSelectFinder(NodeStructTableName)
finder.Append("WHERE "+col+"=?", val)
has, err := zorm.QueryRow(newCtx, finder, &obj)
//添加
node := Node{
Pid: 0,
Name: "cop",
Path: "cop",
Leaf: 0,
Note: "公司節(jié)點",
}
//_, err = DB["mon"].Insert(&node) 這是之前xorm的代碼
//手動開啟事務(wù),匿名函數(shù)返回的error如果不是nil,事務(wù)就會回滾
_, err = zorm.Transaction(newCtx, func(newCtx context.Context) (interface{}, error) {
//具體操作
_, err = zorm.Insert(newCtx, &node)
return nil, err
})
if err != nil {
log.Fatalln("cannot insert node[cop]")
}
//修改
newCtx := getNewCtx("mon")
//手動開啟事務(wù),匿名函數(shù)返回的error如果不是nil,事務(wù)就會回滾
_, err := zorm.Transaction(newCtx, func(newCtx context.Context) (interface{}, error) {
_, err := zorm.Update(newCtx, obj)
//如果返回的err不是nil,事務(wù)就會回滾
return nil, err
})
//刪除
//_, err := DB["mon"].Where("id=?", n.Id).Delete(new(Node))
//手動開啟事務(wù),匿名函數(shù)返回的error如果不是nil,事務(wù)就會回滾
_, err := zorm.Transaction(newCtx, func(newCtx context.Context) (interface{}, error) {
finder := zorm.NewDeleteFinder(NodeStructTableName)
finder.Append("WHERE id=?", n.Id)
_, err := zorm.UpdateFinder(newCtx, finder)
return nil, err
})
改造總結(jié)
zorm上手非常簡單遇八,改造過程也很順暢矛绘。
以上只是zorm使用的部分示例,除此之外zorm還支持事務(wù)傳播刃永、批量操作货矮、不方便使用struct的場景,以及讀寫分離等斯够。更多使用場景請前往 zorm官方囚玫。
如有使用方面的疑問請下方留言,改造建議或想了解更多读规,請在最上方源碼地址找官方抓督,作者回復(fù)也很熱情: )