golang使用sqlx 操作 mysql

sqlx 官方文檔

https://github.com/jmoiron/sqlx

文檔甚好, 基本的 連接和使用都有 , 除了 insert 外最經常使用到的就是 select one 這些比 原生的sql api 好用太多 封裝了 連接 建立關閉 等操作

package main

import (
    "database/sql"
    "fmt"
    "log"
    
    _ "github.com/lib/pq"
    "github.com/jmoiron/sqlx"
)

var schema = `
CREATE TABLE person (
    first_name text,
    last_name text,
    email text
);

CREATE TABLE place (
    country text,
    city text NULL,
    telcode integer
)`

type Person struct {
    FirstName string `db:"first_name"`
    LastName  string `db:"last_name"`
    Email     string
}

type Place struct {
    Country string
    City    sql.NullString
    TelCode int
}

func main() {
    // this Pings the database trying to connect, panics on error
    // use sqlx.Open() for sql.Open() semantics
    db, err := sqlx.Connect("postgres", "user=foo dbname=bar sslmode=disable")
    if err != nil {
        log.Fatalln(err)
    }

    // exec the schema or fail; multi-statement Exec behavior varies between
    // database drivers;  pq will exec them all, sqlite3 won't, ymmv
    db.MustExec(schema)
    
    tx := db.MustBegin()
    tx.MustExec("INSERT INTO person (first_name, last_name, email) VALUES ($1, $2, $3)", "Jason", "Moiron", "[email protected]")
    tx.MustExec("INSERT INTO person (first_name, last_name, email) VALUES ($1, $2, $3)", "John", "Doe", "[email protected]")
    tx.MustExec("INSERT INTO place (country, city, telcode) VALUES ($1, $2, $3)", "United States", "New York", "1")
    tx.MustExec("INSERT INTO place (country, telcode) VALUES ($1, $2)", "Hong Kong", "852")
    tx.MustExec("INSERT INTO place (country, telcode) VALUES ($1, $2)", "Singapore", "65")
    // Named queries can use structs, so if you have an existing struct (i.e. person := &Person{}) that you have populated, you can pass it in as &person
    tx.NamedExec("INSERT INTO person (first_name, last_name, email) VALUES (:first_name, :last_name, :email)", &Person{"Jane", "Citizen", "[email protected]"})
    tx.Commit()

    // Query the database, storing results in a []Person (wrapped in []interface{})
    people := []Person{}
    db.Select(&people, "SELECT * FROM person ORDER BY first_name ASC")
    jason, john := people[0], people[1]

    fmt.Printf("%#v\n%#v", jason, john)
    // Person{FirstName:"Jason", LastName:"Moiron", Email:"[email protected]"}
    // Person{FirstName:"John", LastName:"Doe", Email:"[email protected]"}

    // You can also get a single result, a la QueryRow
    jason = Person{}
    err = db.Get(&jason, "SELECT * FROM person WHERE first_name=$1", "Jason")
    fmt.Printf("%#v\n", jason)
    // Person{FirstName:"Jason", LastName:"Moiron", Email:"[email protected]"}

    // if you have null fields and use SELECT *, you must use sql.Null* in your struct
    places := []Place{}
    err = db.Select(&places, "SELECT * FROM place ORDER BY telcode ASC")
    if err != nil {
        fmt.Println(err)
        return
    }
    usa, singsing, honkers := places[0], places[1], places[2]
    
    fmt.Printf("%#v\n%#v\n%#v\n", usa, singsing, honkers)
    // Place{Country:"United States", City:sql.NullString{String:"New York", Valid:true}, TelCode:1}
    // Place{Country:"Singapore", City:sql.NullString{String:"", Valid:false}, TelCode:65}
    // Place{Country:"Hong Kong", City:sql.NullString{String:"", Valid:false}, TelCode:852}

    // Loop through rows using only one struct
    place := Place{}
    rows, err := db.Queryx("SELECT * FROM place")
    for rows.Next() {
        err := rows.StructScan(&place)
        if err != nil {
            log.Fatalln(err)
        } 
        fmt.Printf("%#v\n", place)
    }
    // Place{Country:"United States", City:sql.NullString{String:"New York", Valid:true}, TelCode:1}
    // Place{Country:"Hong Kong", City:sql.NullString{String:"", Valid:false}, TelCode:852}
    // Place{Country:"Singapore", City:sql.NullString{String:"", Valid:false}, TelCode:65}

    // Named queries, using `:name` as the bindvar.  Automatic bindvar support
    // which takes into account the dbtype based on the driverName on sqlx.Open/Connect
    _, err = db.NamedExec(`INSERT INTO person (first_name,last_name,email) VALUES (:first,:last,:email)`, 
        map[string]interface{}{
            "first": "Bin",
            "last": "Smuth",
            "email": "[email protected]",
    })

    // Selects Mr. Smith from the database
    rows, err = db.NamedQuery(`SELECT * FROM person WHERE first_name=:fn`, map[string]interface{}{"fn": "Bin"})

    // Named queries can also use structs.  Their bind names follow the same rules
    // as the name -> db mapping, so struct fields are lowercased and the `db` tag
    // is taken into consideration.
    rows, err = db.NamedQuery(`SELECT * FROM person WHERE first_name=:first_name`, jason)
}

項目中的實際應用

成熟的項目沒有不使用連接池的, 節省打開關閉的系統開銷

連接

package mysqler

import (	
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"
	"log"
)

var DbInit = DbWorkerConnect{}
type DbWorkerConnect struct {
	Conn *sqlx.DB
}
func NewDbWorkerConnect() {
	dataSourceName := fmt.Sprintf("%v:%v@tcp(%v:%v)/%v", config.GlobalConfig.MySQLUserName, config.GlobalConfig.MySQLPassword, config.GlobalConfig.MySQLHost, config.GlobalConfig.MySQLPort, config.GlobalConfig.MySQLDbName)

	dataSourceName = dataSourceName + "?parseTime=true&loc=Asia%2FShanghai&charset=utf8"
	db, err := sqlx.Connect("mysql", dataSourceName)
	if err != nil {
		log.Fatalln(err)
	}
	db.SetMaxIdleConns(0) // 設置理想最大連接數
	DbInit.Conn = db
}

在操作mysql 的時候的使用方法

在進行初始化 了之後 在程序中就可以直接使用

mysqler.DbInit.Conn.Get(out, queryString)

重點強調坑

  • dateTime 類型
type Demo struct {
	Id              string    `json:"id"`
	CreatedAt       time.Time `json:"createdAt" db:"createdAt"`
	ChangedAt       time.Time `json:"changedAt" db:"changedAt"`
	Timestamp       int64     `json:"timestamp"`	
}

原生的 sql 是說支持的

https://github.com/go-sql-driver/mysql/#installation

但是這裏要注意 一定要在 數據庫 連接的時候 使用相應的參數

dataSourceName = dataSourceName + "?parseTime=true&loc=Asia%2FShanghai&charset=utf8"

parseTime=true 這樣才能獲取到想要的數據格式

存入數據的時候 存成 北京時間

  • 標籤

    FirstName string db:"first_name"

當有兩個單詞組合的時候一定要加上 db:"first_name" 標籤的 不然讀取不到

  • sql.NullString
type Place struct {
    Country string
    City    sql.NullString
    TelCode int
}

當數據庫中設計的字段 默認是 NULL 的時候會用到的。

獲取具體的值

Place .city.String

插入的時候 會使用 這樣的

tx.MustExec("INSERT INTO place (country, city, telcode) VALUES ($1, $2, $3)", "United States", "New York", "1")

讀取的時候 需要什麼字段 取什麼字段 這樣有時候 也好排查錯誤
同時對於性能的影響也是有的

	var opts []modelBaseData.Place
	queryString := fmt.Sprintf("select country ,city from %v where isValid =%v and parentId in (%v)order by opNo asc ", tools.DBEvalQuestion, tools.ValidEvalQuestionTrue, city)
	err := classObj.Db.Conn.Select(&opts, queryString)
  • 插入語句 使用方便的方式
insertSql := fmt.Sprintf("insert into %v (id,"+
		"createdAt,"+
		"changedAt,"+
		"timestamp,"+
		"content,"+
		"isValid) values (:id,"+
		":createdAt,"+
		":changedAt,"+
		":timestamp,"+
		":content,"+
		":isValid)", tools.Demo)
  • 數據庫字段設計成bit 類型 比較坑

struct 中 接受插入都是slice

state[]uint8 `json:"isValid" db:"state"`

插入的時候 要進行轉化


tools.GetByteByInt(1)

func GetByteByInt(obj int) []byte {
	d := []byte(string(obj))
	return d
}

完整的插入語句

	insertSql := fmt.Sprintf("insert into %v (id,"+
		"createdAt,"+
		"changedAt,"+
		"timestamp,"+
		"content,"+
		"state) values (:id,"+
		":createdAt,"+
		":changedAt,"+
		":timestamp,"+
		":content,"+
		":state)", tools.Demo)
	obj.Id = tools.GetUUID()
	obj.CreatedAt = tools.GetNowTimeBj()
	obj.ChangedAt = tools.GetNowTimeBj()
	obj.Timestamp = tools.GetNowTimeStamp()
	obj.IsValid = tools.GetByteByInt(tools.ValidMatchTipTrue)
	tx := DbInit.Db.Conn.MustBegin()
	_, err := tx.NamedExec(insertSql, obj)
	logs.PrintContextLog.Info(err)
	err = tx.Commit()

update 語句

	updateSql := fmt.Sprintf("update %v set "+
		"changedAt = :changedAt,"+
		"timestamp	= :timestamp,"+
		"state=:state"+
		" where id=:id", tools.Demo)
  • select 語句時候注意地方, 如果是字符串的類型就要加上 ‘’ 如果是整型的就不加
queryString := fmt.Sprintf("select id from %v where id ='%v' and state= %v", "yang", theId, 1)

注意 表的名字不需要 加 引號

update 中的語句

updateSql := fmt.Sprintf("update %v set isValid=:isValid where id=:id", demo)
  • in 語句的使用 和注意的地方

當條件 爲string 的時候, 拼接 串

func GetSqlInString(s []string) string {
	var temp []string

	sCount := len(s)
	if sCount == 0 {
		return ""
	}
	for _, item := range s {
		temp = append(temp, "'"+item+"'")
	}
	tempString := strings.Join(temp, ",")
	return tempString
}

爲int64 的 時候

func GetSqlInInt64(s []int64) string {
	var temp []string

	sCount := len(s)
	if sCount == 0 {
		return ""
	}
	for _, item := range s {
		temp = append(temp, ""+strconv.FormatInt(item, 10)+"")
	}
	tempString := strings.Join(temp, ",")
	return tempString
}

調用

parentsString := tools.GetSqlInString(obj)

queryString := fmt.Sprintf("select id from %v where state=%v and parentId in (%v)order by optionNo asc ", "demo", 1, parentsString)
  • 分頁語句
字符串格式化語句
.... order by questionNo asc limit %v offset %v
  • one 方法使用注意
    當獲取不到數據的時候返回的 err 不爲空
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章