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