golang操作mysql使用总结

https://www.cnblogs.com/hanyouchun/

讲解的很详细~

前言

Golang 提供了database/sql包用于对SQL数据库的访问, 作为操作数据库的入口对象sql.DB, 主要为我们提供了两个重要的功能:

  • sql.DB 通过数据库驱动为我们提供管理底层数据库连接的打开和关闭操作.
  • sql.DB 为我们管理数据库连接池

需要注意的是,sql.DB表示操作数据库的抽象访问接口,而非一个数据库连接对象;它可以根据driver打开关闭数据库连接,管理连接池。正在使用的连接被标记为繁忙,用完后回到连接池等待下次使用。所以,如果你没有把连接释放回连接池,会导致过多连接使系统资源耗尽。

操作mysql

1.导入mysql数据库驱动

1234
import (   "database/sql"   _ "github.com/go-sql-driver/mysql")

通常来说, 不应该直接使用驱动所提供的方法, 而是应该使用 sql.DB, 因此在导入 mysql 驱动时, 这里使用了匿名导入的方式(在包路径前添加 _), 当导入了一个数据库驱动后, 此驱动会自行初始化并注册自己到Golang的database/sql上下文中, 因此我们就可以通过 database/sql 包提供的方法访问数据库了.

2.连接数据库

123456789101112131415161718
type DbWorker struct {    //mysql data source name    Dsn string }

func main() {    dbw := DbWorker{        Dsn: "user:password@tcp(127.0.0.1:3306)/test",    }	    db, err := sql.Open("mysql",        dbw.Dsn)    if err != nil {        panic(err)        return    }    defer db.Close()}

通过调用sql.Open函数返回一个sql.DB指针; sql.Open函数原型如下:

1
func Open(driverName, dataSourceName string) (*DB, error)
  • driverName: 使用的驱动名. 这个名字其实就是数据库驱动注册到 database/sql 时所使用的名字.
  • dataSourceName: 数据库连接信息,这个连接包含了数据库的用户名, 密码, 数据库主机以及需要连接的数据库名等信息.
  1. sql.Open并不会立即建立一个数据库的网络连接, 也不会对数据库链接参数的合法性做检验, 它仅仅是初始化一个sql.DB对象. 当真正进行第一次数据库查询操作时, 此时才会真正建立网络连接;
  2. sql.DB表示操作数据库的抽象接口的对象,但不是所谓的数据库连接对象,sql.DB对象只有当需要使用时才会创建连接,如果想立即验证连接,需要用Ping()方法;
  3. sql.Open返回的sql.DB对象是协程并发安全的.
  4. sql.DB的设计就是用来作为长连接使用的。不要频繁Open, Close。比较好的做法是,为每个不同的datastore建一个DB对象,保持这些对象Open。如果需要短连接,那么把DB作为参数传入function,而不要在function中Open, Close。

3.数据库基本操作

数据库查询的一般步骤如下:

  1. 调用 db.Query 执行 SQL 语句, 此方法会返回一个 Rows 作为查询的结果
  2. 通过 rows.Next() 迭代查询数据.
  3. 通过 rows.Scan() 读取每一行的值
  4. 调用 db.Close() 关闭查询

现有user数据库表如下:

123456
CREATE TABLE `user` (  `id` bigint(20) NOT NULL AUTO_INCREMENT,  `name` varchar(20) DEFAULT '',  `age` int(11) DEFAULT '0',  PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4
MySQL 5.5 之前, UTF8 编码只支持1-3个字节,从MYSQL5.5开始,可支持4个字节UTF编码utf8mb4,一个字符最多能有4字节,utf8mb4兼容utf8,所以能支持更多的字符集;关于emoji表情的话mysql的utf8是不支持,需要修改设置为utf8mb4,才能支持。

查询数据

12345678910111213141516171819202122232425262728
func (dbw *DbWorker) QueryData() {	dbw.QueryDataPre()	rows, err := dbw.Db.Query(`SELECT * From user where age >= 20 AND age < 30`)	defer rows.Close()	if err != nil {		fmt.Printf("insert data error: %v\n", err)		return	}	for rows.Next() {		rows.Scan(&dbw.UserInfo.Id, &dbw.UserInfo.Name, &dbw.UserInfo.Age)		if err != nil {			fmt.Printf(err.Error())			continue		}		if !dbw.UserInfo.Name.Valid {			dbw.UserInfo.Name.String = ""		}		if !dbw.UserInfo.Age.Valid {			dbw.UserInfo.Age.Int64 = 0		}		fmt.Println("get data, id: ", dbw.UserInfo.Id, " name: ", dbw.UserInfo.Name.String, " age: ", int(dbw.UserInfo.Age.Int64))	}

	err = rows.Err()	if err != nil {		fmt.Printf(err.Error())	}}
  1. rows.Scan 参数的顺序很重要, 需要和查询的结果的column对应. 例如 “SELECT * From user where age >=20 AND age < 30” 查询的行的 column 顺序是 “id, name, age” 和插入操作顺序相同, 因此 rows.Scan 也需要按照此顺序 rows.Scan(&id, &name, &age), 不然会造成数据读取的错位.
  2. 因为golang是强类型语言,所以查询数据时先定义数据类型,但是查询数据库中的数据存在三种可能:存在值,存在零值,未赋值NULL 三种状态, 因为可以将待查询的数据类型定义为sql.Nullxxx类型,可以通过判断Valid值来判断查询到的值是否为赋值状态还是未赋值NULL状态.
  3. 每次db.Query操作后, 都建议调用rows.Close(). 因为 db.Query() 会从数据库连接池中获取一个连接, 这个底层连接在结果集(rows)未关闭前会被标记为处于繁忙状态。当遍历读到最后一条记录时,会发生一个内部EOF错误,自动调用rows.Close(),但如果提前退出循环,rows不会关闭,连接不会回到连接池中,连接也不会关闭, 则此连接会一直被占用. 因此通常我们使用 defer rows.Close() 来确保数据库连接可以正确放回到连接池中; 不过阅读源码发现rows.Close()操作是幂等操作,即一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同, 所以即便对已关闭的rows再执行close()也没关系.

单行查询

123456
var name stringerr = db.QueryRow("select name from user where id = ?", 1).Scan(&name)if err != nil {    log.Fatal(err)}fmt.Println(name)
  1. err在Scan后才产生,上述链式写法是对的
  2. 需要注意Scan()中变量和顺序要和前面Query语句中的顺序一致,否则查出的数据会映射不一致.

插入数据

12345678910111213
func (dbw *DbWorker) insertData() {	ret, err := dbw.Db.Exec(`INSERT INTO user (name, age) VALUES ("xys", 23)`)	if err != nil {		fmt.Printf("insert data error: %v\n", err)		return	}	if LastInsertId, err := ret.LastInsertId(); nil == err {		fmt.Println("LastInsertId:", LastInsertId)	}	if RowsAffected, err := ret.RowsAffected(); nil == err {		fmt.Println("RowsAffected:", RowsAffected)	}}

通过db.Exec()插入数据,通过返回的err可知插入失败的原因,通过返回的ret可以进一步查询本次插入数据影响的行数RowsAffected和最后插入的Id(如果数据库支持查询最后插入Id).

github完整代码示例

4.预编译语句(Prepared Statement)
预编译语句(PreparedStatement)提供了诸多好处, 因此我们在开发中尽量使用它. 下面列出了使用预编译语句所提供的功能:

  • PreparedStatement 可以实现自定义参数的查询
  • PreparedStatement 通常来说, 比手动拼接字符串 SQL 语句高效.
  • PreparedStatement 可以防止SQL注入攻击

一般用Prepared StatementsExec()完成INSERTUPDATEDELETE操作。

下面是将上述案例用Prepared Statement 修改之后的完整代码

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
package main

import (	"database/sql"	"fmt"	_ "github.com/go-sql-driver/mysql")

type DbWorker struct {	Dsn      string	Db       *sql.DB	UserInfo userTB}type userTB struct {	Id   int	Name sql.NullString	Age  sql.NullInt64}

func main() {	var err error	dbw := DbWorker{		Dsn: "root:123456@tcp(localhost:3306)/sqlx_db?charset=utf8mb4",	}	dbw.Db, err = sql.Open("mysql", dbw.Dsn)	if err != nil {		panic(err)		return	}	defer dbw.Db.Close()

	dbw.insertData()	dbw.queryData()}

func (dbw *DbWorker) insertData() {	stmt, _ := dbw.Db.Prepare(`INSERT INTO user (name, age) VALUES (?, ?)`)	defer stmt.Close()

	ret, err := stmt.Exec("xys", 23)	if err != nil {		fmt.Printf("insert data error: %v\n", err)		return	}	if LastInsertId, err := ret.LastInsertId(); nil == err {		fmt.Println("LastInsertId:", LastInsertId)	}	if RowsAffected, err := ret.RowsAffected(); nil == err {		fmt.Println("RowsAffected:", RowsAffected)	}}

func (dbw *DbWorker) QueryDataPre() {	dbw.UserInfo = userTB{}}func (dbw *DbWorker) queryData() {	stmt, _ := dbw.Db.Prepare(`SELECT * From user where age >= ? AND age < ?`)	defer stmt.Close()

	dbw.QueryDataPre()

	rows, err := stmt.Query(20, 30)	defer rows.Close()	if err != nil {		fmt.Printf("insert data error: %v\n", err)		return	}	for rows.Next() {		rows.Scan(&dbw.UserInfo.Id, &dbw.UserInfo.Name, &dbw.UserInfo.Age)		if err != nil {			fmt.Printf(err.Error())			continue		}		if !dbw.UserInfo.Name.Valid {			dbw.UserInfo.Name.String = ""		}		if !dbw.UserInfo.Age.Valid {			dbw.UserInfo.Age.Int64 = 0		}		fmt.Println("get data, id: ", dbw.UserInfo.Id, " name: ", dbw.UserInfo.Name.String, " age: ", int(dbw.UserInfo.Age.Int64))	}

	err = rows.Err()	if err != nil {		fmt.Printf(err.Error())	}}

[转帖]golang操作mysql使用总结的更多相关文章

  1. golang操作mysql数据库

    golang操作mysql数据库 代码: mysql的增.删.改.查 package main import ( "database/sql" "fmt" &q ...

  2. golang操作mysql使用总结

    前言 Golang 提供了database/sql包用于对SQL数据库的访问, 作为操作数据库的入口对象sql.DB, 主要为我们提供了两个重要的功能: sql.DB 通过数据库驱动为我们提供管理底层 ...

  3. Golang操作MySQL的正确姿势

    封装原因: 查看了很多网上提供的ORM类型的数据库操作,觉得比较麻烦,需要提前配置很多的表结构体,然后才能使用,对于数据表很多的项目就配置起来就比较麻烦,所以对golang的mysql包进行了外层包装 ...

  4. Golang 操作mysql使用举例---连接本地数据库

    连接数据库的方式有两种:TCP和Unix域socket. 本文使用Unix domain sockets连接数据库.关于TCP连接数据库可以参考Go 操作mysql使用举例 下面例子中,演示了使用sh ...

  5. Mysql学习(一)添加一个新的用户并用golang操作Mysql

    Mysql添加一个新的用户并赋予权限 添加一个自己的用户到mysql 首先我们需要先用root用户登录mysql,但是刚安装完没有密码,我们先跳过密码 ailumiyana@ailumiyana:~/ ...

  6. golang操作mysql

    1. 安装mysql驱动库和sqlx基于官方sql库的扩展库 go get github.com/go-sql-driver/mysql go get github.com/jmoiron/sqlx ...

  7. go语言之行--golang操作redis、mysql大全

    一.redis 简介 redis(REmote DIctionary Server)是一个由Salvatore Sanfilippo写key-value存储系统,它由C语言编写.遵守BSD协议.支持网 ...

  8. Go基础之--操作Mysql(一)

    关于标准库database/sql database/sql是golang的标准库之一,它提供了一系列接口方法,用于访问关系数据库.它并不会提供数据库特有的方法,那些特有的方法交给数据库驱动去实现. ...

  9. Golang原生sql操作Mysql数据库增删改查

    Golang要操作mysql数据库,首先需要在当期系统配置GOPATH,因为需要使用go get命令把驱动包下载到GOPATH下使用. 首先配置好你的GOPATH,执行以下命令,下载安装mysql驱动 ...

随机推荐

  1. [我还会回来的]asp.net core再战iris

    废话不多说,直接开干! 硬件配置 处理器: Intel(R) Core(TM) i5-4690k CPU @3.90GHz 内存容量: 8.00 GB 软件版本 OS: Microsoft Windo ...

  2. 服务器控件的几个属性 SelectedIndex、SelectedItem、SelectedValue、SelectedItem.Text、selectedItem.value

    转自http://blog.csdn.net/iqv520/article/details/4419186 1. SelectedIndex ——选项的索引,为int,从0开始,可读可写 2. Sel ...

  3. [PHP] curl访问https与CA证书问题

    CA证书,用来在调用HTTPS资源的时候,验证对方网站是否是CA颁布的证书,而不是自己随便生成的 curl命令1.需要下载CA证书 文件地址是 http://curl.haxx.se/ca/cacer ...

  4. JDK8新特性:default方法的应用实践

    背景: 最近维护一个老旧工程,遇到集团层面的数据安全改造,需要在DAO层做加解密改造.而这个老旧工程的DAO层是用的JdbcTemplate实现的,尽管template方式实现起来可自由发挥的空间很大 ...

  5. C# 消息队列-MSMQ

    MQ是一种消息中间件技术,所以它能够支持多种类型的语言开发,同时也是跨平台的通信机制,也就是说MQ支持将信息转化为XML或者JSon等类型的数据存储到消息队列中,然后可以使用不同的语言来处理消息队列中 ...

  6. 【代码笔记】Web-CSS-CSS background背景

    一,效果图. 二,代码. <!DOCTYPE html> <html> <head> <meta charset="utf-8"> ...

  7. Dynamics 365-如何下载新版本的Tools

    最近新写了个Plugin,想注册到CRM中,但是使用旧版的PluginRegistration Tool的时候,总是加载不出来Plugin数据,所以寻思下载个最新版的Tool试试.现在下载Tools的 ...

  8. Cube的高级设置

    分享来源地址:http://bigdata.51cto.com/art/201705/538648.htm Cube的高级设置 随着维度数目的增加,Cuboid 的数量会爆炸式地增长.为了缓解 Cub ...

  9. python3 Queue(单向队列)

    创建队列 import queue q = queue.Queue() empty(如果队列为空,返回True) import queue q = queue.Queue() print(q.empt ...

  10. keepalived+nginx负载均衡+ApacheWeb实现高可用

    1.Keepalived高可用软件 Keepalived软件起初是专为LVS负载均衡软件设计的,用来管理并监控LVS集群系统中各个服务节点的状态,后来又加入了可以实现高可用的VRRP功能.因此,kee ...