go操作DB需要注意的

百里江山 · · 2340 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

准备

CREATE TABLE `nation` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  `name` varchar(25) DEFAULT '' COMMENT '国家名称',
  `age` int(11) unsigned DEFAULT NULL COMMENT '年龄',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;

INSERT INTO `nation` VALUES ('1', '', '3');
INSERT INTO `nation` VALUES ('2', '2', null);

连接库的操作

func main() {
    db, err := sql.Open("mysql", "root:root@tcp(127.0.0.1:3306)/test")
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()
    Find(db)
    First(db)
}

处理sql.ErrNoRows错误

只有获取单行数据时,Scan没有匹配到结果集则会报sql.ErrNoRows错误。
https://github.com/golang/go/blob/master/src/database/sql/sql.go#L3138

一般处理 , 见(1)处

func First(db *sql.DB) {
    sqlText := "select `id`, `age` from nation where id = ?"
    stmt, err := db.Prepare(sqlText)
    if err != nil {
        log.Fatal(err)
    }
    var (
        id  int32
        age int32
    )
    err = stmt.QueryRow(1).Scan(&id, &age)
    if err != nil && err != sql.ErrNoRows { // (1)过滤空的情况 
        log.Fatal(err)
    }
    fmt.Printf("ID:%d, value:%d\n", id, age)
}

mysql 字段为null如何处理

如nation表里的age字段, DEFAULT NULL。 (2)处报错

func First(db *sql.DB) {
    sqlText := "select `id`, `age` from nation where id = ?"
    stmt, err := db.Prepare(sqlText)
    if err != nil {
        log.Fatal(err)
    }
    var (
        id  int32
        age int32  
    )
    err = stmt.QueryRow(2).Scan(&id, &age) //(2)age 处报错
    if err != nil && err != sql.ErrNoRows { 
        log.Fatal(err)
    }
    fmt.Printf("ID:%d, value:%d\n", id, age)
}

直接报错:字段age为null, 不支持操作,因为age是整型。
2020/04/03 19:03:55 sql: Scan error on column index 1, name "age": converting NULL to int32 is unsupported

解决方法

见(3), (4)处解决

func First(db *sql.DB) {
    sqlText := "select `id`, `age` from nation where id = ?"
    stmt, err := db.Prepare(sqlText)
    if err != nil {
        log.Fatal(err)
    }
    var (
        id  int32
        age sql.NullInt32 // (3) 修改前 int32
    )
    err = stmt.QueryRow(2).Scan(&id, &age)
    if err != nil && err != sql.ErrNoRows {
        log.Fatal(err)
    }
    fmt.Printf("ID:%d, value:%d\n", id, age.Int32) // (4) 修改前age
}

destination not a pointer 目标不是指针

func First(db *sql.DB) {
    sqlText := "select `id`, `age` from nation where id = ?"
    stmt, err := db.Prepare(sqlText)
    if err != nil {
        log.Fatal(err)
    }
    var (
        id  int32
        age sql.NullInt32
    )
    err = stmt.QueryRow(2).Scan(&id, age) // (4) Scan要使用地址赋值,所以报错。
    if err != nil && err != sql.ErrNoRows {
        log.Fatal(err)
    }
    fmt.Printf("ID:%d, value:%d\n", id, age.Int32)
}

报错:
2020/04/03 19:10:26 sql: Scan error on column index 1, name "age": destination not a pointer

解决方法:变量前加&
err = stmt.QueryRow(2).Scan(&id, &age)

关闭 rows

  1. QueryRow 无须关闭
  2. Query+Next+Scan需要关闭。
func Find(db *sql.DB) {
    sqlText := "select `id`, `age` from nation where id = ?"
    stmt, err := db.Prepare(sqlText)
    if err != nil {
        log.Fatal(err)
    }
    rows, err := stmt.Query(21)
    if err != nil {
        log.Fatal(err)
    }
    defer rows.Close() // 关闭,防止进一步的枚举
    var (
        id    int
        value int
    )
    list := make(map[int]int)
    for rows.Next() {
        err = rows.Scan(&id, &value)
        if err != nil {
            log.Fatal(err)
        }
        list[id] = cast.ToInt(value)
    }
    fmt.Println(list)
}

总结果

  1. Scan 操作时需要使用地址,变量前加&
  2. 获取单行数据时会报sql.ErrNoRows错误。Next
  3. mysql字段为null时,使用sql.Null*操作
  4. Query+Next+Scan需要关闭。QueryRow 自带关闭

有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:百里江山

查看原文:go操作DB需要注意的

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

2340 次点击  
加入收藏 微博
下一篇:2.安装
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传