Go官方支持对SQL数据库的基本操作,举例连接MySQL!
dbconfig.go
:数据库配置文件【因为同一个包下所有数据都是互通的,所以统一管理连接配置!注意:驱动名必须是相应的数据库(因为驱动包init()时会将数据库名和驱动注册,且同一种数据库驱动注册一次就好!)】
package db
import (
"database/sql"
_ "github.com/go-sql-driver/mysql" //注册驱动器
)
var err error
// 主数据库配置(写数据库)
var wdb *sql.DB
const (
wDriverName = "mysql" //驱动名必须是相应的数据库
wUsername = "root"
wPassword = "xxxxxx"
wProtocol = "tcp"
wAddress = "127.0.0.1:3306"
wDbname = "student"
)
// 从数据库配置(读数据库)
var rdb *sql.DB
const (
rDriverName = "mysql" //驱动名必须是相应的数据库
rUsername = "root"
rPassword = "xxxxxx"
rProtocol = "tcp"
rAddress = "127.0.0.2:3306"
rDbname = "student"
)
readmysql.go
读数据库连接:
package db
import (
"database/sql"
"fmt"
"sync"
"time"
)
func ConnReadMySQL()
// 数据源名
dsn := rUsername + ":" + rPassword + "@" + rProtocol + "(" + rAddress + ")" + "/" + rDbname
rdb, err = sql.Open(rDriverName, dsn)
if err != nil {
panic(err)
}
// 数据库设置
rdb.SetConnMaxLifetime(time.Minute * 3)
rdb.SetConnMaxIdleTime(time.Minute * 2)
rdb.SetMaxOpenConns(10)
rdb.SetMaxIdleConns(10)
// 连接测试
err = rdb.Ping()
if err != nil {
fmt.Println("Database:", rDriverName, "test connected failed!")
panic(err)
}
fmt.Println("Database:", rDriverName, "test connected successfully!")
}
writemysql.go
写数据库连接:
package db
import (
"database/sql"
"fmt"
"sync"
"time"
)
func ConnWriteMySQL() {
// 数据源名
dsn := wUsername + ":" + wPassword + "@" + wProtocol + "(" + wAddress + ")" + "/" + wDbname
wdb, err = sql.Open(wDriverName, dsn)
if err != nil {
panic(err)
}
// 数据库设置
wdb.SetConnMaxLifetime(time.Minute * 3)
wdb.SetConnMaxIdleTime(time.Minute * 2)
wdb.SetMaxOpenConns(10)
wdb.SetMaxIdleConns(10)
// 连接测试
err = wdb.Ping()
if err != nil {
fmt.Println("Database:", wDriverName, "test connected failed!")
panic(err)
}
fmt.Println("Database:", wDriverName, "test connected successfully!")
}
$ go get -u github.com/go-sql-driver/mysql
会下载到pkg文件夹中!
注意:sql.Open()
创建的sql.DB
是线程安全的,且维护一个连接池!
只记:
查询:Query
修改:Exec
预编译:prepare
事务:Begin() (*Tx, error)
type DB
:代表底层连接池,sql包会负责管理连接池,多go下是安全的。一旦调用了BD.Begin,返回的Tx会绑定到单个连接。当调用事务Tx的Commit或Rollback后,该事务使用的连接会归还到DB的闲置连接池中。Open(driverName, dataSourceName string) (*DB, error)
:创建DB【注意:driverName必须是对应的数据库名称】数据库连接维护都是sql.DB
,所以通过DB传入SQL进行操作!
注意:Scan(&a.id)
,且Scan(...)
参数要和查询结果一一对应!
单行查询:QueryRow
(底层就是Context方法:QueryRowContext(ctx context.Context, ...) *Row
)
QueryRow(query string, args ...interface{}) *Row
:QueryRow总是返回非nil的值!Row
的 Scan(dest ...interface{})
如果多行会返回第一行,如果没有则会返回err
// GetAdmin 查询一行数据
func GetAdmin(id int) (*Admin,error) {
row := Rdb.QueryRow("select * from admin where id=?;",id)
a := new(Admin)
e := row.Scan(&a.id,&a.username,&a.password)
return a,e
}
多行查询:Query
(底层就是Context方法:QueryContext(ctx context.Context,..) (*Rows, error)
)
Query(query string, args ...interface{}) (*Rows, error)
:返回多行结果Rows
的 Next()
从第一行开始检测是否有数据(指针的next就是第一行)Rows
的 Scan(dest ...interface{})
扫描一次,指针移动一次defer rows.Close()
// GetAdmins 查询多行数据
func GetAdmins(offset int,size int) ([]*Admin,error) {
admins := make([]*Admin,5)
rows,e := Rdb.Query("select * from admin limit ?,?;",offset,size)
if e != nil {
return nil, e
}
defer rows.Close()
for rows.Next() {
admin := new(Admin)
e = rows.Scan(&admin.id,&admin.username,&admin.password)
if e != nil {
return nil, e
}
admins = append(admins, admin)
}
return admins,e
}
包括插入、删除、更新!
Result
type Result interface {
/ LastInsertId返回一个数据库生成的回应命令的整数。
LastInsertId() (int64, error)
/ RowsAffected返回命令影响的行数。
RowsAffected() (int64, error)
}
Exec(query string, args ...interface{}) (Result, error)
:执行命令底层就是Context方法:ExecContext(ctx context.Context, query string, args ...interface{}) (Result, error)
func (a *Admin) Insert() {
result,err := Wdb.Exec("insert into admin(username,password) values(?,?)",a.username,a.password)
if err != nil {
log.Fatal(err)
}
id,_ := result.LastInsertId() /插入值的id
num,_ := result.RowsAffected() /影响的行数
fmt.Println(id,num)
}
Ping() error
:a connection to the database is still alive?
底层还是PingContext(ctx context.Context) error
Prepare(query string) (*Stmt, error)
:预编译语句
Stmt
可多次调用预编译语句!
stmt, err := db.Prepare("INSERT INTO projects(id, mascot, release, category) VALUES( ?, ?, ?, ? )")
if err != nil {
log.Fatal(err)
}
defer stmt.Close() // Prepared statements take up server resources and should be closed after use.
for id, project := range projects {
if _, err := stmt.Exec(id+1, project.mascot, project.release, "open source");
err != nil {
log.Fatal(err)
}
}
开启事务: func (db *DB) Begin() (*Tx, error)
事务中: 使用Tx
结构体执行操作
回滚事务: func (tx *Tx) Rollback() error
执行操作过程中发生错误,则回滚事务
提交事务: (tx *Tx) Commit() error
func (a *Admin) transaction() {
/ 开启事务
tx,err := Wdb.Begin()
if err != nil {
log.Fatal(err.Error())
}
_,err = tx.Exec("insert into admin(username,password) values(?,?)",a.username,a.password)
if err != nil {
/ 发生错误则回滚
if rollbackErr := tx.Rollback(); rollbackErr != nil {
log.Fatal(rollbackErr.Error())
}
log.Fatal(err)
}
_,err = tx.Exec("upadte admin set password=? where id=?","aaa",1)
if err != nil {
/ 发生错误则回滚
if rollbackErr := tx.Rollback(); rollbackErr != nil {
log.Fatal(rollbackErr.Error())
}
log.Fatal(err)
}
/ 提交事务
if err := tx.Commit(); err != nil {
log.Fatal(err)
}
}
Go官方不提供NoSQL数据库操作,选用第三方轮子!