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 不为空

你可能感兴趣的:(golang)