golang實現rest server框架(二)

第二篇:golang數據庫增刪改操做具體實現(mysql)

背景

這篇文章是golang針對數據庫增刪改(非查詢結果集,查詢語句的自動生成比較複雜,下篇文章專門解析)操做具體實現,包括了自動生成sql與自定義sql相關函數,以及指的插入與更新,同時實現了異常處理。java

一些關鍵點

  1. 利用panic與recover實現數據庫異常處理。
  2. 函數可變參數的解析。
  3. 批量插入與更新使用同一個函數。
  4. 全部更新sql語句參數化。

代碼解析

按功能模塊對核心代碼進行說明node

異常處理

golang語言沒有異常處理,但能夠經過panic、recover及defer來實現,值得注意的一點是,如何在defer中返回相應的信息給上層函數。
//rs要在這定義,defer中修改rs的信息才能返回到上層調用函數
func execute(sql string, values []interface{}) (rs map[string]interface{}) {
    log.Println(sql, values)
    rs = make(map[string]interface{})        //我本來rs是在這聲明並定義的,結果返回爲空
    defer func() {
        if r := recover(); r != nil {
            rs["code"] = 500                //仔細想來,兩個返回路徑,一個是正常return,一個是聲明中的rs返回值
            rs["err"] = "Exception, " + r.(error).Error()
        }
    }()
    ...
    //這其中的代碼若引起了panic,在返回上層調用函數前會執行defer
    ...
    return rs
}

非查詢操做的底層封裝函數(execute)

golang的數據庫操做分返回查詢結果集的和無查詢結果集的,沒找到能統一處理的API,象java、node.js同樣,我只能分開封裝了,這裏實現execute。
func execute(sql string, values []interface{}) (rs map[string]interface{}) {
    log.Println(sql, values)
    ...
    //異常處理與數據配置文件讀取
    ...
    //鏈接數據庫
    dao, err := mysql.Open(dialect, dbUser+":"+dbPass+"@tcp("+dbHost+":"+dbPort+")/"+dbName+"?charset="+dbCharset)
    stmt, _ := dao.Prepare(sql)        //預處理
    ers, err := stmt.Exec(values...)   //提供參數並執行
    if err != nil {
        rs["code"] = 204                //錯誤處理
        rs["err"] = err.Error()
    } else {
        id, _ := ers.LastInsertId()        //自動增加ID的最新值,若插入
        affect, _ := ers.RowsAffected()    //影響的行數
        rs["code"] = 200
        rs["info"] = sql[0:6] + " operation success."
        rs["LastInsertId"] = id
        rs["RowsAffected"] = affect

    }
    return rs                         //成功返回
}
參數說明:
  • sql,調用這個函數時,要麼已經自動生成了標準的sql,要麼就自定義的sql,全部的語句要求是參數化形式的
  • values,參數列表,與sql中的佔位符一一對應

新增函數的實現(Insert)

數據新增操做的具體實現,這個是根據用戶提交的json數據自動生成標準sql的函數。
func Insert(tablename string, params map[string]interface{}) map[string]interface{} {
    values := make([]interface{}, 0)
    sql := "INSERT INTO `" + tablename + "` (" //+strings.Join(allFields, ",")+") VALUES ("
    var ks []string
    var vs []string
    for k, v := range params {            //注意:golang中對象的遍歷,字段的排列是隨機的
        ks = append(ks, "`" + k + "`")    //保存全部字段
        vs = append(vs, "?")              //提供相應的佔位符
        values = append(values, v)        //對應保存相應的值
    }
    //生成正常的插入語句
    sql += strings.Join(ks, ",") + ") VALUES (" + strings.Join(vs, ",") + ")"
    return execute(sql, values)
}

修改函數的實現(Update)

數據修改操做的具體實現,這個是根據用戶提交的json數據自動生成標準sql的函數。
func Update(tablename string, params map[string]interface{}, id string) map[string]interface{} {
    values := make([]interface{}, 0)
    sql := "UPDATE `" + tablename + "` set " //+strings.Join(allFields, ",")+") VALUES ("
    var ks string
    index := 0
    psLen := len(params)
    for k, v := range params {        //遍歷對象
        index++
        values = append(values, v)    //參數
        ks += "`" + k + "` =  ?"      //修改一個key的語句
        if index < psLen {            //非最後一個key,加逗號
            ks += ","
        }
    }
    values = append(values, id)      //主鍵ID是單獨的
    sql += ks + " WHERE id = ? "
    return execute(sql, values)
}

刪除函數的實現(Delete)

數據刪除操做的具體實現。
func Delete(tablename string, id string) map[string]interface{} {
    sql := "DELETE FROM " + tablename + " where id = ? "        //只支持單個ID操做,這是自動化的接口,批量操做走其它接口
    values := make([]interface{}, 0)
    values = append(values, id)
    return execute(sql, values)
}

批量新增與修改函數的實現(InsertBatch)

數據批量新增與修改操做的具體實現,兩種方式在同一接口中實現。
func InsertBatch(tablename string, els []map[string]interface{}) map[string]interface{}  {
    values := make([]interface{}, 0)
    sql := "INSERT INTO " + tablename
    var upStr string
    var firstEl map[string]interface{}        //第一個插入或修改的對象
    lenEls := len(els)                        //由於golang對象遍歷的隨機性,咱們取出第一個對象先分析,去除隨機性
    if lenEls > 0 {
        firstEl = els[0]
    }else {                                  //一個元素都沒有,顯然調用參數不對
        rs := make(map[string]interface{})
        rs["code"] = 301
        rs["err"] = "Params is wrong, element must not be empty."
        return rs
    }
    var allKey []string                      //保存一個對象的全部字段,對象訪問時就按這個順序
    eleHolder := "("
    index := 0
    psLen := len(firstEl)
    for k, v := range firstEl {
        index++
        eleHolder += "?"                          //佔位符
        upStr += k + " = values (" + k + ")"      //更新操做時的字段與值對應關係
        if index < psLen {                        //非最後一個key
            eleHolder += ","
            upStr += ","
        }else{
            eleHolder += ")"
        }
        allKey = append(allKey, k)               //key
        values = append(values, v)               //value
    }
    //批量操做的第一個對象語句的自動生成
    sql += " ("+strings.Join(allKey, ",")+") values " + eleHolder

    for i := 1; i < lenEls; i++ {             //依據對第一個對象的分析,生成全部的後續對象
        sql += "," + eleHolder
        for _, key := range allKey {
            values = append(values, els[i][key])
        }
    }
    //當主鍵或惟一索引存在時,進行更新操做的sql語句生成
    sql += " ON DUPLICATE KEY UPDATE " + upStr
    return execute(sql, values)
}

bock.go(程序入口)

這裏提供一些用於測試的代碼。

用於測試的數據表結構python

CREATE TABLE `books` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '惟一性索引',
  `name` varchar(64) DEFAULT '' COMMENT '名稱',
  `isbn` varchar(64) DEFAULT '' COMMENT '圖書ISBN',
  `u_id` int(11) DEFAULT '0' COMMENT '用戶ID',
  `status` tinyint(4) DEFAULT '1' COMMENT '狀態:0-禁;1-有效;9刪除',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '建立時間',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時間',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uuid` (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='表';

新增測試mysql

params := make(map[string] interface{})
    args := make(map[string] interface{})
    session := make(map[string] interface{})
    session["userid"] = "112"
    args["session"] = session
    params["name"] = "golang實戰"
    params["isbn"] = "41563qtrs5-X"
    params["status"] = 1
    db := &table
    rs := db.Create(params, args)
    fmt.Println(rs)

修改測試git

params = make(map[string] interface{})
    args = make(map[string] interface{})
    args["id"] = 2
    params["name"] = "golang實戰,修改了"
    params["status"] = 3
    rs = db.Update(params, args)
    fmt.Println(rs)

刪除測試github

args = make(map[string] interface{})
    args["id"] = 1
    rs = db.Delete(nil, args)
    fmt.Println(rs)

批量測試golang

vs := make([]map[string]interface{}, 0)

    params := make(map[string] interface{})
    params["name"] = "golang批量11213"        //第一個對象
    params["isbn"] = "4156s5"
    params["status"] = 5
    params["id"] = 9
    vs = append(vs, params)

    params = make(map[string] interface{})
    params["name"] = "golang批量22af24"        //第二個對象
    params["isbn"] = "xxfqwt325rqrf45"
    params["status"] = 2
    params["id"] = 10
    vs = append(vs, params)

    db := &table
    rs := db.InsertBatch("books", vs)
    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來實現,指望有驚喜,寫代碼我是認真的。sql

相關文章
相關標籤/搜索