golang实现rest server框架(二)

news/2024/12/14 19:12:48 标签: golang, 数据库, java

第二篇: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(程序入口)

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

用于测试的数据表结构

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来实现,期望有惊喜,写代码我是认真的。


http://www.niftyadmin.cn/n/1588631.html

相关文章

稀疏自动编码之反向传播算法(BP)

假设给定m个训练样本的训练集&#xff0c;用梯度下降法训练一个神经网络&#xff0c;对于单个训练样本(x,y)&#xff0c;定义该样本的损失函数&#xff1a; 那么整个训练集的损失函数定义如下&#xff1a; 第一项是所有样本的方差的均值。第二项是一个归一化项&#xff08;也叫…

android sdk目录结构

Android Sdk 目录结构示意图 一、add-ons这里面保存着附加库&#xff0c;比如GoogleMaps&#xff0c;当然你如果安装了OphoneSDK&#xff0c;这里也会有一些类库在里面。 二、build-tools 个版本的sdk工具&#xff0c;当你新建android项目的时候会用到这个包。 主要是包一些…

spring中的@import注解

2019独角兽企业重金招聘Python工程师标准>>> 当前使用版本 jdk:1.8 springboot:2.0.2操作部分 方法1 Import 该注解的方式为 引入一个类或者多个类到spring容器内&#xff1b;//在spring扫描不到的包里面定义一个类 public class Broker {public void testBroker()…

Android 电量变化监听

Intent.ACTION_BATTERY_CHANGED This is a sticky broadcast containing the charging state, level, and other information about the battery. See BatteryManager for documentation on the contents of the Intent. 你不能像组件那样在manifests里声明一个receive ,你只能…

剑指Offer 反转链表

时间限制&#xff1a;1秒 空间限制&#xff1a;32768K 热度指数&#xff1a;281981本题知识点&#xff1a; 链表算法知识视频讲解 题目描述 输入一个链表&#xff0c;反转链表后&#xff0c;输出新链表的表头。给出代码&#xff1a;/* struct ListNode {int val;struct ListNod…

Windows 10体验:第一时间下载了Windows10开始安装

装了一台Lenovo Yoga 13超级本&#xff0c;安装速度很快&#xff01;后来发现在Windows 8.1的Hyper-V虚拟机中也可以使用触控&#xff0c;但有些触控操作不容易确认的还是在超极本上测试了一下&#xff0c;发现&#xff0c;跟虚拟机中一样。Build 9841Windows 10 技术预览版体验…

围观2018DAMS中国数据资产管理峰会

围观2018DAMS中国数据资产管理峰会主办方、赞助阵容和支持媒体都很不错啊&#xff0c;AWS、腾讯云、IBM等都去了另外赞助阵容没有阿里云、华为云&#xff1f;创蓝253短信服务平台和数据服务平台开启大数据布局&#xff0c;也在赞助之列...转载于:https://blog.51cto.com/137506…

实现动态获取Android手机CPU架构类型

1、什么是动态库(.so文件)&#xff1f;.so文件是unix的动态连接库&#xff0c;是二进制文件&#xff0c;作用相当于windows下的.dll文件。他使用了C/C代码编写的可以操作硬件比java更高级的底层代码&#xff0c;执行速度和效率比其他语言要高。在Android中调用动态库文件(*.so)…