go操作DB需要注意的

语言: CN / TW / HK

准备

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 自带关闭
分享到: