专栏名称: 狗厂
目录
相关文章推荐
NXTV都市阳光  ·  提醒 | 宁夏家长注意:这种疫苗开始接种了! ·  18 小时前  
作家张萌  ·  张萌:想要健康又漂亮,这7本书必读 ·  3 天前  
作家张萌  ·  张萌:想要健康又漂亮,这7本书必读 ·  3 天前  
51好读  ›  专栏  ›  狗厂

golang实现rest server框架(二)

狗厂  · 掘金  ·  · 2018-07-05 10:35

正文

第二篇:golang数据库增删改操作具体实现(mysql)

背景

这篇文章是golang针对数据库增删改(非查询结果集,查询语句的自动生成比较复杂,下篇文章专门解析)操作具体实现,包括了自动生成sql与自定义sql相关函数,以及指的插入与更新,同时实现了异常处理。

一些关键点

  1. 利用panic与recover实现数据库异常处理。
  2. 函数可变参数的解析。
  3. 批量插入与更新使用同一个函数。
  4. 所有更新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(程序入口)

这里提供一些用于测试的代码。

用于测试的数据表结构







请到「今天看啥」查看全文


推荐文章