golang实现rest server框架(二)
第二篇:golang数据库增删改操作具体实现(mysql)
背景
这篇文章是golang针对数据库增删改(非查询结果集,查询语句的自动生成比较复杂,下篇文章专门解析)操作具体实现,包括了自动生成sql与自定义sql相关函数,以及指的插入与更新,同时实现了异常处理。
一些关键点
- 利用panic与recover实现数据库异常处理。
- 函数可变参数的解析。
- 批量插入与更新使用同一个函数。
- 所有更新sql语句参数化。
代码解析
按功能模块对核心代码进行说明
异常处理
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(程序入口)
这里提供一些用于测试的代码。用于测试的数据表结构
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='表';
新增测试
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)
修改测试
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)
删除测试
args = make(map[string] interface{}) args["id"] = 1 rs = db.Delete(nil, args) fmt.Println(rs)
批量测试
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来实现,期望有惊喜,写代码我是认真的。
相关推荐
GoatSucker 2020-11-23
CoderToy 2020-11-16
蛰脚踝的天蝎 2020-11-10
ZGCdemo 2020-11-09
86193952 2020-10-27
Lzs 2020-10-23
hjr 2020-10-21
HELLOBOY 2020-10-21
qidiantianxia 2020-10-21
cmsmdn 2020-10-21
myspace 2020-10-21
笑面依旧 2020-10-21
专注前端开发 2020-10-21
技术之博大精深 2020-10-16
zhushenghan 2020-11-09
风雨断肠人 2020-10-13
xuguiyi00 2020-11-04
sichenglain 2020-10-27