Go连接数据库

SQL数据库

Go官方支持对SQL数据库的基本操作,举例连接MySQL!

官方:README

案例:连接多个数据库

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文件夹中!

二、匿名引入驱动器

Go连接数据库_第1张图片

三、获取sql.DB

注意:sql.Open()创建的sql.DB是线程安全的,且维护一个连接池

  1. sql.DB采用懒加载!,并不会真的连接。
  2. 建议在同包下直接使用sql.DB!
  3. sql.DB不需要关闭!

Go连接数据库_第2张图片

sql.DB的常见方法

只记:
查询:Query
修改:Exec
预编译:prepare
事务:Begin() (*Tx, error)

DB结构体

  1. type DB:代表底层连接池,sql包会负责管理连接池,多go下是安全的。一旦调用了BD.Begin,返回的Tx会绑定到单个连接。当调用事务Tx的Commit或Rollback后,该事务使用的连接会归还到DB的闲置连接池中。
  2. Open(driverName, dataSourceName string) (*DB, error) :创建DB【注意:driverName必须是对应的数据库名称】

CRUD

数据库连接维护都是sql.DB,所以通过DB传入SQL进行操作!

查询

注意:Scan(&a.id),且Scan(...)参数要和查询结果一一对应!

查询函数

单行查询:QueryRow(底层就是Context方法:QueryRowContext(ctx context.Context, ...) *Row

  1. QueryRow(query string, args ...interface{}) *Row:QueryRow总是返回非nil的值!
  2. RowScan(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))

  1. Query(query string, args ...interface{}) (*Rows, error):返回多行结果
  2. RowsNext()从第一行开始检测是否有数据(指针的next就是第一行)
  3. RowsScan(dest ...interface{}) 扫描一次,指针移动一次
  4. 注意:必须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

重复利用预编译sql

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)
	}
}

第三方:GORM

就是使用费GORM框架代替代码中的sql语句!
Go连接数据库_第3张图片

Go连接数据库_第4张图片

Go连接数据库_第5张图片



第三方:Go连接Redis

Go官方不提供NoSQL数据库操作,选用第三方轮子!

你可能感兴趣的:(Go进阶,mysql,go)