用golang對數據庫標準操做進行封裝,爲後面的rest server提供數據庫訪問層。實現的目標是:能根據rest請求參數自動生成數據庫操做語句,提供增、刪、改、查、批量寫入、事務等必要的數據庫操做封裝。並能夠方便的擴展到多種數據庫,讓全部的數據庫操做對於rest server來講表現爲一致的訪問接口。java
按功能模塊對核心代碼進行說明node
數據庫標準操做接口定義,根據個人實踐經驗,如下的接口設計已經可以很好的支持大部分的數據庫操做,這些操做包括了根據json參數自動完成的CURD、手寫sql支持、批量插入(更新)心及事務操做。
type IBock interface{ //根據參數,自動完成數據庫查詢 Retrieve(params map[string]interface{}, args ...interface{}) map[string]interface{} //根據參數,自動完成數據庫插入 Create(params map[string]interface{}, args ...interface{}) map[string]interface{} //根據參數,自動完成數據庫更新(只支持單條) Update(params map[string]interface{}, args ...interface{}) map[string]interface{} //根據參數,自動完成數據庫刪除(只支持單條) Delete(params map[string]interface{}, args ...interface{}) map[string]interface{} //手寫查詢sql支持 QuerySql(sql string, values []interface{}, params map[string]interface{}) map[string]interface{} //手寫非查詢sql支持 ExecSql(sql string, values []interface{}) map[string]interface{} //批量插入或更新 InsertBatch(tablename string, els []interface{}) map[string]interface{} //事務支持 TransGo(objs map[string]interface{}) map[string]interface{} }
參數說明
接口的具體實現,本文是對mysql的實現,暫只實現了基本的CURD,項目中會逐步完善。
//咱們把操做對象定義在一個表上 type Bock struct { Table string } //parseArgs函數的功能是解析args參數中包括的可變參數,實如今下面 func (b *Bock) Retrieve(params map[string]interface{}, args ...interface{}) map[string]interface{} { //查詢時咱們通常只關注查詢哪些表字段 _, fields, _ := parseArgs(args) //調用具體的查詢接口,查詢接口將根據輸入參數params自動實現sql查詢語句,支持多樣的查詢定義,如:lks(從多個字體查詢相同內容),ors(或查詢),ins(in查詢)等 return Query(b.Table, params, fields) } func (b *Bock) Create(params map[string]interface{}, args ...interface{}) map[string]interface{} { //新建接口,通常都會關注用戶在session的ID _, _, session := parseArgs(args) uId := session["userid"].(string) params["u_id"] = uId //調用具體的插入接口 return Insert(b.Table, params) } func (b *Bock) Update(params map[string]interface{}, args ...interface{}) map[string]interface{} { //只支持單個更新,因此ID必須存在 id, _, _ := parseArgs(args) if len(id) == 0 { rs := make(map[string]interface{}) rs["code"] = 301 rs["err"] = "Id must be input." return rs } return Update(b.Table, params) } func (b *Bock) Delete(params map[string]interface{}, args ...interface{}) map[string]interface{} { //只支持單個刪除,因此ID必須存在 id, _, _ := parseArgs(args) if len(id) == 0 { rs := make(map[string]interface{}) rs["code"] = 301 rs["err"] = "Id must be input." return rs } return Delete(b.Table, params) }
parseArgs函數的實現
func parseArgs(args []interface{}) (string, []string, map[string]interface{}) { //解析指定的參數 var id string //信息ID var fields []string //查詢字段集 var session map[string]interface{} //用戶session對象 for _, vs := range args { switch vs.(type) { case map[string]interface{}: //只接收指定類型 for k, v := range vs.(map[string]interface{}) { if k == "id" { id = v.(string) } if k == "fields" { fields = v.([]string) } if k == "session" { session = v.(map[string]interface{}) } } default: } } return id, fields, session //返回解析成功的參數 }
數據操做的具體實現,大可能是僞代碼,項目後續會逐步完善,查詢接口最重要,後面會有單獨文章進行解析
func Query(tablename string, params map[string]interface{}, fields []string ) map[string]interface{} { //調用具體實現的私用函數,接口中分自動和手動兩個函數,在私用函數中屏蔽差別內聚功能 return query(tablename, params, fields, "", nil) } func Insert(tablename string, params map[string]interface{}) map[string]interface{} { sql := "Insert into " + tablename values := make([]interface{},0) return execute(sql, values) } func Update(tablename string, params map[string]interface{}) map[string]interface{} { sql := "Update " + tablename + " set " values := make([]interface{},0) return execute(sql, values) } func Delete(tablename string, params map[string]interface{}) map[string]interface{} { sql := "Delete from " + tablename + " where" values := make([]interface{},0) return execute(sql, values) }
私用查詢函數定義
//五個輸入參數,分別適配自動與手動查詢 func query(tablename string, params map[string]interface{}, fields []string, sql string, vaules []interface{}) map[string]interface{} { if vaules == nil { vaules = make([]interface{},0) } //調用真正的數據庫操做函數 return execQeury("select "+ strings.Join(fields, ",")+" from " + tablename, vaules) }
非查詢類具體操做函數
//由於golang把有結果集的和無結果集的操做是分開的,不象在java或node.js中,能夠有高級函數進行統一操做,只能分開。 func execute(sql string, values []interface{}) map[string]interface{} { //返回json對象,以map形式表達 rs := make(map[string]interface{}) rs["code"] = 200 return rs }
查詢類具體操做(已經實現),結果集以json對象封裝,存儲在map中
func execQeury(sql string, values []interface{}) map[string]interface{} { var configs interface{} ...//省略數據配置獲取代碼,請參照之前的文章 dao, err := mysql.Open(dialect, dbUser + ":"+dbPass+"@tcp("+dbHost+":"+dbPort+")/"+dbName+"?charset="+dbCharset) stmt, err := dao.Prepare(sql) rows, err := stmt.Query(values...) columns, err := rows.Columns() //取出字段名稱 vs := make([]mysql.RawBytes, len(columns)) scans := make([]interface{}, len(columns)) for i := range vs { //預設取值地址 scans[i] = &vs[i] } var result []map[string]interface{} for rows.Next() { _ = rows.Scan(scans...) //塡入一列值 each := make(map[string]interface{}) for i, col := range vs { if col != nil { each[columns[i]] = string(col) //增值 }else{ each[columns[i]] = nil } } result = append(result, each) } rs["code"] = 200 //data, _ := json.Marshal(result) //這樣就能轉換爲json rs["rows"] = result return rs }
數據庫的批量操做,在前面的文章中已經用golang實現,只是還未封裝,有興趣的朋友能夠看我前面的文章。
最終目標的入口將是一個網絡服務,提供標準的restful服務,如今只是用來測試,再這說明一下願景。
table := Bock.Bock{ //上體實例 Table: "role", //對role表時行操做 } var params map[string] interface{} //模擬json參數 args := make(map[string] interface{}) //其它參數 db := make([]DB.IBock, 1) //對接口編程 db[0] = &table //接口指向實例對象,這裏能夠現時處理多個不一樣的實例 fields := []string {"id", "name"} args["fields"] = fields rs, _ := db[0].Retrieve(params, args) //在這能夠循環處理多個不一樣的實例,咱們最終的目標就是在這接受用戶的http請求,由路由自動分發不一樣的請求,咱們的數據庫封裝自動生成sql語句完成用戶的基本需求。 fmt.Println(rs)
https://github.com/zhoutk/goTools
git clone https://github.com/zhoutk/goTools cd goTools go get go run bock.go go buid bock.go ./bock
通過多種方案的對比,發現go語言做爲網絡服務的吞吐率是最棒的,因此有了將以往在其它平臺上的經驗(node.js,java,python3),用go來實現,指望有驚喜,寫代碼我是認真的。python