导入包
在golang.org中选择packages,搜索mysql
选择第一个, 根据其中提示进行导入
提示 在shell中输入
$ go get -u github.com/go-sql-driver/mysql
(需要安装git)
若出现以下提示:
PS D:\goLang\mysql> go get -u github.com/go-sql-driver/mysql
go: go.mod file not found in current directory or any parent directory.
'go get' is no longer supported outside a module.
To build and install a command, use 'go install' with a version,
like 'go install example.com/cmd@latest'
For more information, see https://golang.org/doc/go-get-install-deprecation
or run 'go help get' or 'go help install'.
说明go get命令在module之外不再获得支持,需要在当前进行go module 的初始化,来建立相关的go mod文件,命令如下
go mod init 项目名
此时可以继续进行操作
出现上面提示,导包成功
获得数据库的连接
sql.open不会直接建立一个连接,因此不会因为服务器是否可用或者密码、用户名不正确而报错。因此,使用db.ping来验证是否可用
Open打开一个dirverName指定的数据库,dataSourceName指定数据源,一般至少包括数据库文件名和其它连接必要的信息。
package main
import (
"database/sql"
"time"
_ "github.com/go-sql-driver/mysql"
)
func main() {
// See "Important settings" section.
//db, err := sql.Open("mysql", "user:password@/dbname")
db, err := sql.Open("mysql", "root:123456@/itheima")
//根据自己的设置填入用户名和密码,纯测试数据库名可以不填,保留最后的'/'即可
db.SetConnMaxLifetime(time.Minute * 3)
db.SetMaxOpenConns(10)
db.SetMaxIdleConns(10)
if err != nil {
panic(err.Error()) // Just for example purpose. You should use proper error handling instead of panic
}
defer db.Close()
//-------------------------------------
// OPEN不会创建数据库连接,使用ping来验证DSN数据:
err = db.Ping()//-----------------------------
if err != nil {
panic(err.Error()) // proper error handling instead of panic in your app
}
// Use the DB normally, execute the querys etc
//...
}
SetMaxOpenConns
设置与数据库建立连接的最大数目。 如果n大于0且小于最大闲置连接数,会将最大闲置连接数减小到匹配最大开启连接数的限制。 如果n<=0,不会限制最大开启连接数,默认为0(无限制)。
SetMaxIdleConns设置连接池中的最大闲置连接数。 如果n大于最大开启连接数,则新的最大闲置连接数会减小到匹配最大开启连接数的限制。 如果n<=0,不会保留闲置连接。
我们可以将这部分代码封装为一个函数,方便后续操作。这部分即为DBinit,封装为DBsql包
为了后续使用,需要返回相关数据库接口db
返回的DB对象可以安全地被多个goroutine并发使用,并且维护其自己的空闲连接池。因此,Open函数应该仅被调用一次,很少需要关闭这个DB对象。
package DBsql
import (
"database/sql"
"fmt"
"time"
_ "github.com/go-sql-driver/mysql"
)
// 初始化数据库
func DBinit() *sql.DB {
// See "Important settings" section.
db, err := sql.Open("mysql", "root:123456@/itheima")
db.SetConnMaxLifetime(time.Minute * 3)
db.SetMaxOpenConns(10)
db.SetMaxIdleConns(10)
if err != nil {
panic(err.Error()) // Just for example purpose. You should use proper error handling instead of panic
}
defer db.Close()
// Open doesn't open a connection. Validate DSN data:
err = db.Ping()
if err != nil {
panic(err.Error()) // proper error handling instead of panic in your app
}
fmt.Println("连接成功")
return db
// Use the DB normally, execute the querys etc
}
插入数据
我们定义一个全局变量db
,用来保存数据库连接对象。将上面的示例代码拆分出一个独立的initDB
函数,只需要在程序启动时调用一次该函数完成全局变量db的初始化,其他函数中就可以直接使用全局变量db
了。
为了方便后续操作,先在mysql中建立test表:
create table test(
id int primary key auto_increment comment '编号',
name varchar(3) comment '姓名',
age int comment '年龄'
)comment '用户表';
插入、更新和删除操作都是用Exec方法
func (db *DB) Exec(query string, args ...any) (Result, error)
这里实例如下:
func main() {
//初始化连接
db = DBsql.InitDB()
//最后 断开连接
defer db.Close()
//-----------------------------------------------------------------------------
//第一句为DML语句,其中‘?’为相关参数,在后面进行补充
ret, err := db.Exec("insert into test (name,age) values (?,?)", "赵云", 22)
//-----------------------------------------------------------------------------
if err != nil {
fmt.Println("插入失败")
fmt.Printf("err: %v\n", err)
} else {
fmt.Println("ok!")
}
//这里查询新插入数据的ID
i, err2 := ret.LastInsertId()
if err2 != nil {
fmt.Println("get lastInsertID failed")
}
fmt.Printf("insert success, ID is: %v\n", i)
}
执行结果如下:
[Running] go run "c:\Users\wang2\go\mysql\main.go"
连接成功
ok!
insert success, ID is: 1
可以将其 封装为insert_test函数,第一个参数为姓名,第二个参数为年龄
func main() {
//初始化连接
db = DBsql.InitDB()
//最后 断开连接
defer db.Close()
insert_test("王二", 23)
}
func insert_test(name string, age int) {
//第一句为DML语句,其中‘?’为相关参数,在后面进行补充
ret, err := db.Exec("insert into test (name,age) values (?,?)", name, age)
if err != nil {
fmt.Println("插入失败")
fmt.Printf("err: %v\n", err)
} else {
fmt.Println("ok!")
}
//这里查询新插入数据的ID
i, err2 := ret.LastInsertId()
if err2 != nil {
fmt.Println("get lastInsertID failed")
}
fmt.Printf("insert success, ID is: %v\n", i)
}
运行结果:
[Running] go run "c:\Users\wang2\go\mysql\main.go"
连接成功
ok!
insert success, ID is: 2
查询操作
// 定义 表内内容 的结构体,方便后续查询
type test struct {
ID int
name string
age int
}
单行查询
单行查询db.QueryRow()
执行一次查询,并期望返回最多一行结果(即Row)。QueryRow总是返回非nil的值,直到返回值的Scan方法被调用时,才会返回被延迟的错误。
QueryRow(query string, args ...any) *sql.Row
实例:
func QueryOneRow() {
var t test
//queryrow后调用scan方法进行读取,并且释放数据库连接
err := db.QueryRow("select * from test where id = ?", 1).Scan(&t.ID, &t.name, &t.age)
if err != nil {
fmt.Printf("err: %v\n", err)
}
fmt.Printf("t: %v\n", t)
}
查询结果:
[Running] go run "c:\Users\wang2\go\mysql\main.go"
连接成功
t: {1 赵云 22}
多行查询
多行查询db.Query()
执行一次查询,返回多行结果(即Rows),一般用于执行select命令。参数args表示query中的占位参数。
func (*sql.DB).Query(query string, args ...any) (*sql.Rows, error)
这里返回的rows不能直接scan,会提示调用next:
[Running] go run "c:\Users\wang2\go\mysql\main.go"
连接成功
err2: sql: Scan called without calling Next
这里实例如下:
func QueryRows() {
var t test
r, err := db.Query("select * from test where age > ?", 20)
if err != nil {
fmt.Printf("err: %v\n", err)
}
for r.Next() {
err2 := r.Scan(&t.ID, &t.name, &t.age)
if err2 != nil {
fmt.Printf("err2: %v\n", err2)
}
fmt.Printf("t: %v\n", t)
}
}
运行结果:
[Running] go run "c:\Users\wang2\go\mysql\main.go"
连接成功
t: {1 赵云 22}
t: {2 王二 23}
更新数据
如插入数据,更新数据使用Exec方法
func (db *DB) Exec(query string, args ...any) (Result, error)
实例:
func UpdateData() {
r, err := db.Exec("update test set name = ? where id =?", "王小波", 2)
if err != nil {
fmt.Println("数据更新失败,", err)
}
//获得影响的行数
i, _ := r.RowsAffected()
fmt.Println("更新数据成功,更新行数:", i)
}
结果:
[Running] go run "c:\Users\wang2\go\mysql\main.go"
连接成功
t: {1 赵云 22}
t: {2 王二 23}
更新数据成功,更新行数: 1
删除数据
如插入数据,更新数据使用Exec方法
func (db *DB) Exec(query string, args ...any) (Result, error)
示例:
func DeleteData() {
r, err := db.Exec("delete from test where id = ?", 1)
if err != nil {
fmt.Println("删除成功")
}
i, _ := r.RowsAffected()
fmt.Println("删除行数:", i)
}
结果:
[Running] go run "c:\Users\wang2\go\mysql\main.go"
连接成功
t: {1 赵云 22}
t: {2 王小波 23}
更新数据成功,更新行数: 0
删除行数: 1