這篇文章是golang針對數據庫增刪改(非查詢結果集,查詢語句的自動生成比較複雜,下篇文章專門解析)操做具體實現,包括了自動生成sql與自定義sql相關函數,以及指的插入與更新,同時實現了異常處理。java
按功能模塊對核心代碼進行說明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 }
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 //成功返回 }
參數說明:
數據新增操做的具體實現,這個是根據用戶提交的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) }
數據修改操做的具體實現,這個是根據用戶提交的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) }
數據刪除操做的具體實現。
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) }
數據批量新增與修改操做的具體實現,兩種方式在同一接口中實現。
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) }
這裏提供一些用於測試的代碼。
用於測試的數據表結構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