Golang操做MySQL

[TOC] #數據庫 不少時候咱們都是把數據存儲在文件中,例如:xxx.txt,讀取都須要對文件進行操做,那若是數據特別多,咱們就得建立N個文件,而且很是很差管理,性能也差,因此就出來了數據庫這個概念,數據庫本生也是一套軟件系統,它存儲數據的本質仍是文件,有了數據庫這套系統,咱們只須要學會怎麼去使用這套系統便可mysql

常見的數據庫:SQLlite、MySQL、Mongodb、Oraclegit

#MySQL簡介 MySQL是一個開源的關係型數據庫,它擁有很強大的SQL語句,咱們經過SQL語句實現數據的增刪改查。github

SQL語句 DDL:操做數據庫 DML:表的增刪改查 DCL:用戶及權限sql

存儲引擎 MySQL支持插件式的存儲引擎,常見的有:MyISAM和InnoDB。數據庫

MyISAM:安全

  • 查詢速度快
  • 只支持表鎖
  • 不支持事務 InnoDB:
  • 總體速度快
  • 支持表鎖和行鎖
  • 支持事務(多個SQL操做當成一個總體)

#Go操做MySQL Go語言中的database/sql包提供了保證SQL或類SQL數據庫的範接口,並不提供具體的數據庫驅動。使用database/sql包時必須注入(至少)一個數據庫驅動,原生支持鏈接池,是併發安全的。bash

下載驅動服務器

go get -u github.com/go-sql-driver/mysql

使用MySQL驅動併發

package main

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

var db *sql.DB // 是一個鏈接池對象

func initDB()(err error){
	// 數據庫信息
	dsn := "root:123456@tcp(10.4.7.51:3306)/test_sql"
	// 鏈接數據庫
	db, err = sql.Open("mysql",dsn) // 不會校驗用戶名和密碼是否正確
	if err != nil {		// dsn 格式不正確的時候會報錯
		return
	}
	err = db.Ping() // 嘗試鏈接數據庫
	if err != nil {
		return
	}
	return
}


// Go鏈接MySQL示例
func main(){
	err := initDB()
	if err !=nil {
		fmt.Printf("init DB failed,err: %v\n",err)
		return
	}
	fmt.Println("鏈接數據庫成功")
}

設置數據庫最大鏈接數tcp

db.SetMaxOpenConns(100)

設置數據庫最大閒置鏈接數

db.SetMaxIdleConns(10)

#CRUD ##建庫建表 咱們先在MySQL中建立一個名爲test_sql的數據庫

CREATE DATABASE test_sql

進入數據庫

user test_sql

執行如下命令建立用於測試的表結構

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=1 DEFAULT CHARSET=utf8mb4;

插入多條測試數據

insert into user(name,age) values("Jack",9000);
insert into user(name,age) values("Xander",31);
insert into user(name,age) values("Eric",25);
insert into user(name,age) values("Alim",21);
insert into user(name,age) values("小明",22);
insert into user(name,age) values("大力哥",30);

#查詢 ##單行查詢 單行查詢db.QueryRow()執行一次查詢,並最多返回去一行結果(ROW)。QueryRow老是返回非nil的值,直到返回值的Scan方法被調用時,纔會返回去被延遲的錯誤(如:未找到結果)

func queryOnceDemo(id int){
	var u1 user
	// 寫查詢單條記錄的SQL語句
	sqlStr := `select id,name,age from user where id=?`
	// 執行
	db.QueryRow(sqlStr,id).Scan(&u1.id,&u1.name,&u1.age) // 從鏈接池裏拿一個鏈接出來去數據庫查詢單條記錄
	// 打印結構
	fmt.Printf("u1:%#v\n",u1)
}

##多行查詢 多行查詢db.Query()執行一次查詢,返回多行結果(即Rows),通常用於運行select命令。

func queryMultDemo(n int){
	// sql語句
	sqlStr := `select id,name,age from user where id > ?;`
	// 執行
	rows, err := db.Query(sqlStr,n)
	if err != nil{
		fmt.Sprintf("exec %s query failled,err:%v\n",sqlStr,err)
		return
	}
	// 必定要關閉rows
	defer rows.Close()
	// 循環取值
	for rows.Next() {
		var u1 user
		err := rows.Scan(&u1.id,&u1.name,&u1.age)
		if err != nil {
			fmt.Printf("scan failed,err:%v\n",err)
		}
		fmt.Printf("u1:%#v\n",u1)
	}
}

#插入數據 Exec執行一次命令(包括查詢、刪除、更新、插入等),返回的Result是對已執行的SQL命令的總結

func insertRowDemo(){
	// 寫SQL語句
	sqlStr := `insert into user(name,age) values("老王",28)`
	// exec
	ret, err := db.Exec(sqlStr)
	if err != nil {
		fmt.Printf("insert failed,err:%v\n",err)
		return
	}
	// 若是是插入數據的操做,可以拿到插入數據的id
	id, err := ret.LastInsertId()
	if err != nil {
		fmt.Printf("get last id failed,err:$v",err)
		return
	}
	fmt.Println("id:",id)
}

#更新數據

func updateRowDemo(newAge,id int){
	sqlStr := `update user set age=? where id =?`
	ret, err := db.Exec(sqlStr,newAge,id)
	if err != nil {
		fmt.Printf("update failed,err:%v\n",err)
		return
	}
	n, err := ret.RowsAffected()
	if err != nil {
		fmt.Printf("get last id failed,err:$v",err)
		return
	}
	fmt.Printf("update row :%v\n",n)
}

#刪除數據

func deleteRowDemo(id int){
	sqlStr := `delete from user where id=?`
	ret, err := db.Exec(sqlStr,id)
	if err != nil {
		fmt.Printf("delete failed,err:%v\n",err)
		return
	}
	n, err := ret.RowsAffected()
	if err != nil {
		fmt.Printf("get last id failed,err:$v",err)
		return
	}
	fmt.Printf("delete row :%v\n",n)
}

#MySQL預處理 優化MySQL服務端重複執行SQL的方法,能夠提高服務器性能,提早讓服務端編譯,一次編譯屢次執行,節省後續編譯的成本,也能避免SQL注入的問題。

Go中的Prepare方法會先將sql語句發送到MySQL服務端,返回一個準備好的狀態用於以後的查詢和命令。返回值能夠同時執行多個查詢和命令。

查詢操做的預處理示例代碼以下:

// 預處理查詢示例
func prepareQueryDemo() {
	sqlStr := "select id, name, age from user where id > ?"
	stmt, err := db.Prepare(sqlStr)
	if err != nil {
		fmt.Printf("prepare failed, err:%v\n", err)
		return
	}
	defer stmt.Close()
	rows, err := stmt.Query(0)
	if err != nil {
		fmt.Printf("query failed, err:%v\n", err)
		return
	}
	defer rows.Close()
	// 循環讀取結果集中的數據
	for rows.Next() {
		var u user
		err := rows.Scan(&u.id, &u.name, &u.age)
		if err != nil {
			fmt.Printf("scan failed, err:%v\n", err)
			return
		}
		fmt.Printf("id:%d name:%s age:%d\n", u.id, u.name, u.age)
	}
}

插入、更新和刪除操做的預處理十分相似,這裏以插入操做的預處理爲例:

// 預處理插入示例
func prepareInsertDemo() {
	sqlStr := "insert into user(name, age) values (?,?)"
	stmt, err := db.Prepare(sqlStr)
	if err != nil {
		fmt.Printf("prepare failed, err:%v\n", err)
		return
	}
	defer stmt.Close()
	_, err = stmt.Exec("小王子", 18)
	if err != nil {
		fmt.Printf("insert failed, err:%v\n", err)
		return
	}
	_, err = stmt.Exec("沙河娜扎", 18)
	if err != nil {
		fmt.Printf("insert failed, err:%v\n", err)
		return
	}
	fmt.Println("insert success.")
}

#MySQL事務操做 什麼是事務? 事務:一個最小的不可再分的工做單元;一般一個事務對應一個完整的業務(例如銀行帳戶轉帳業務,該業務就是一個最小的工做單元),同時這個完整的業務須要執行屢次的DML(insert、update、delete)語句共同聯合完成。A轉帳給B,這裏面就須要執行兩次update操做。

在MySQL中只有使用了Innodb數據庫引擎的數據庫或表才支持事務。事務處理能夠用來維護數據庫的完整性,保證成批的SQL語句要麼所有執行,要麼所有不執行。 事務:一個最小的不可再分的工做單元;一般一個事務對應一個完整的業務(例如銀行帳戶轉帳業務,該業務就是一個最小的工做單元),同時這個完整的業務須要執行屢次的DML(insert、update、delete)語句共同聯合完成。A轉帳給B,這裏面就須要執行兩次update操做。

在MySQL中只有使用了Innodb數據庫引擎的數據庫或表才支持事務。事務處理能夠用來維護數據庫的完整性,保證成批的SQL語句要麼所有執行,要麼所有不執行。

事務相關方法 Go語言中使用如下三個方法實現MySQL中的事務操做。 開始事務

func (db *DB) Begin() (*Tx, error)

提交事務

func (tx *Tx) Commit() error

回滾事務

func (tx *Tx) Rollback() error

事務示例 下面的代碼演示了一個簡單的事務操做,該事物操做可以確保兩次更新操做要麼同時成功要麼同時失敗,不會存在中間狀態。

// 事務操做示例
func transactionDemo() {
	tx, err := db.Begin() // 開啓事務
	if err != nil {
		if tx != nil {
			tx.Rollback() // 回滾
		}
		fmt.Printf("begin trans failed, err:%v\n", err)
		return
	}
	sqlStr1 := "Update user set age=30 where id=?"
	_, err = tx.Exec(sqlStr1, 2)
	if err != nil {
		tx.Rollback() // 回滾
		fmt.Printf("exec sql1 failed, err:%v\n", err)
		return
	}
	sqlStr2 := "Update user set age=40 where id=?"
	_, err = tx.Exec(sqlStr2, 4)
	if err != nil {
		tx.Rollback() // 回滾
		fmt.Printf("exec sql2 failed, err:%v\n", err)
		return
	}
	err = tx.Commit() // 提交事務
	if err != nil {
		tx.Rollback() // 回滾
		fmt.Printf("commit failed, err:%v\n", err)
		return
	}
	fmt.Println("exec trans success!")
}

#sqlx使用 第三方庫sqlx可以簡化操做,提升開發效率。

安裝

go get github.com/jmoiron/sqlx

鏈接數據庫

var db *sqlx.DB

func initDB() (err error) {
	dsn := "user:password@tcp(127.0.0.1:3306)/test"
	// 也可使用MustConnect鏈接不成功就panic
	db, err = sqlx.Connect("mysql", dsn)
	if err != nil {
		fmt.Printf("connect DB failed, err:%v\n", err)
		return
	}
	db.SetMaxOpenConns(20)
	db.SetMaxIdleConns(10)
	return
}

查詢 查詢單行數據示例代碼以下:

// 查詢單條數據示例
func queryRowDemo() {
	sqlStr := "select id, name, age from user where id=?"
	var u user
	err := db.Get(&u, sqlStr, 1)
	if err != nil {
		fmt.Printf("get failed, err:%v\n", err)
		return
	}
	fmt.Printf("id:%d name:%s age:%d\n", u.ID, u.Name, u.Age)
}

查詢多行數據示例代碼以下:

// 查詢多條數據示例
func queryMultiRowDemo() {
	sqlStr := "select id, name, age from user where id > ?"
	var users []user
	err := db.Select(&users, sqlStr, 0)
	if err != nil {
		fmt.Printf("query failed, err:%v\n", err)
		return
	}
	fmt.Printf("users:%#v\n", users)
}

插入、更新和刪除 sqlx中的exec方法與原生sql中的exec使用基本一致:

// 插入數據
func insertRowDemo() {
	sqlStr := "insert into user(name, age) values (?,?)"
	ret, err := db.Exec(sqlStr, "沙河小王子", 19)
	if err != nil {
		fmt.Printf("insert failed, err:%v\n", err)
		return
	}
	theID, err := ret.LastInsertId() // 新插入數據的id
	if err != nil {
		fmt.Printf("get lastinsert ID failed, err:%v\n", err)
		return
	}
	fmt.Printf("insert success, the id is %d.\n", theID)
}

// 更新數據
func updateRowDemo() {
	sqlStr := "update user set age=? where id = ?"
	ret, err := db.Exec(sqlStr, 39, 6)
	if err != nil {
		fmt.Printf("update failed, err:%v\n", err)
		return
	}
	n, err := ret.RowsAffected() // 操做影響的行數
	if err != nil {
		fmt.Printf("get RowsAffected failed, err:%v\n", err)
		return
	}
	fmt.Printf("update success, affected rows:%d\n", n)
}

// 刪除數據
func deleteRowDemo() {
	sqlStr := "delete from user where id = ?"
	ret, err := db.Exec(sqlStr, 6)
	if err != nil {
		fmt.Printf("delete failed, err:%v\n", err)
		return
	}
	n, err := ret.RowsAffected() // 操做影響的行數
	if err != nil {
		fmt.Printf("get RowsAffected failed, err:%v\n", err)
		return
	}
	fmt.Printf("delete success, affected rows:%d\n", n)
}

事務操做 對於事務操做,咱們可使用sqlx中提供的db.Beginx()和tx.MustExec()方法來簡化錯誤處理過程。示例代碼以下:

func transactionDemo() {
	tx, err := db.Beginx() // 開啓事務
	if err != nil {
		if tx != nil {
			tx.Rollback()
		}
		fmt.Printf("begin trans failed, err:%v\n", err)
		return
	}
	sqlStr1 := "Update user set age=40 where id=?"
	tx.MustExec(sqlStr1, 2)
	sqlStr2 := "Update user set age=50 where id=?"
	tx.MustExec(sqlStr2, 4)
	err = tx.Commit() // 提交事務
	if err != nil {
		tx.Rollback() // 回滾
		fmt.Printf("commit failed, err:%v\n", err)
		return
	}
	fmt.Println("exec trans success!")
}
相關文章
相關標籤/搜索