組件分享之后端組件——對(duì)golang數(shù)據(jù)庫(kù)/sql的通用擴(kuò)展組件sqlx

組件分享之后端組件——對(duì)golang數(shù)據(jù)庫(kù)/sql的通用擴(kuò)展組件sqlx

背景

近期正在探索前端蚪黑、后端、系統(tǒng)端各類(lèi)常用組件與工具妨退,對(duì)其一些常見(jiàn)的組件進(jìn)行再次整理一下浓镜,形成標(biāo)準(zhǔn)化組件專(zhuān)題,后續(xù)該專(zhuān)題將包含各類(lèi)語(yǔ)言中的一些常用組件换途。歡迎大家進(jìn)行持續(xù)關(guān)注懊渡。

組件基本信息

內(nèi)容

本節(jié)我們分享一個(gè)對(duì)golang數(shù)據(jù)庫(kù)/sql的通用擴(kuò)展組件sqlx

它在 go 的標(biāo)準(zhǔn)庫(kù)上提供了一組擴(kuò)展 database/sql刽射。sql.DB, sql.TX, sql.Stmt, 等的 sqlx 版本。所有這些都保持底層接口不變剃执,因此它們的接口是標(biāo)準(zhǔn)接口的超集誓禁。這使得使用 database/sql 與 sqlx 集成現(xiàn)有代碼庫(kù)相對(duì)容易。

主要的附加概念是:

  • 將行編組為結(jié)構(gòu)(具有嵌入式結(jié)構(gòu)支持)肾档、映射和切片
  • 命名參數(shù)支持现横,包括準(zhǔn)備好的語(yǔ)句
  • GetSelect快速?gòu)牟樵?xún)轉(zhuǎn)到結(jié)構(gòu)/切片

除了godoc API 文檔之外,還有一些用戶(hù)文檔解釋了如何database/sql與 sqlx 一起使用阁最。
安裝方式如下:

go get github.com/jmoiron/sqlx

使用案例如下:

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() {
    // ping數(shù)據(jù)庫(kù)嘗試連接
    // 使用sqlx.Open()實(shí)現(xiàn)sql.Open()語(yǔ)義
    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", "jmoiron@jmoiron.net")
    tx.MustExec("INSERT INTO person (first_name, last_name, email) VALUES ($1, $2, $3)", "John", "Doe", "johndoeDNE@gmail.net")
    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", "jane.citzen@example.com"})
    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:"jmoiron@jmoiron.net"}
    // Person{FirstName:"John", LastName:"Doe", Email:"johndoeDNE@gmail.net"}

    // 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:"jmoiron@jmoiron.net"}

    // 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": "bensmith@allblacks.nz",
    })

    // 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)
    
    
    // batch insert
    
    // batch insert with structs
    personStructs := []Person{
        {FirstName: "Ardie", LastName: "Savea", Email: "asavea@ab.co.nz"},
        {FirstName: "Sonny Bill", LastName: "Williams", Email: "sbw@ab.co.nz"},
        {FirstName: "Ngani", LastName: "Laumape", Email: "nlaumape@ab.co.nz"},
    }

    _, err = db.NamedExec(`INSERT INTO person (first_name, last_name, email)
        VALUES (:first_name, :last_name, :email)`, personStructs)

    // batch insert with maps
    personMaps := []map[string]interface{}{
        {"first_name": "Ardie", "last_name": "Savea", "email": "asavea@ab.co.nz"},
        {"first_name": "Sonny Bill", "last_name": "Williams", "email": "sbw@ab.co.nz"},
        {"first_name": "Ngani", "last_name": "Laumape", "email": "nlaumape@ab.co.nz"},
    }

    _, err = db.NamedExec(`INSERT INTO person (first_name, last_name, email)
        VALUES (:first_name, :last_name, :email)`, personMaps)
}
本文聲明:

88x31.png

知識(shí)共享許可協(xié)議
本作品由 cn華少 采用 知識(shí)共享署名-非商業(yè)性使用 4.0 國(guó)際許可協(xié)議 進(jìn)行許可戒祠。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市速种,隨后出現(xiàn)的幾起案子姜盈,更是在濱河造成了極大的恐慌,老刑警劉巖配阵,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件馏颂,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡棋傍,警方通過(guò)查閱死者的電腦和手機(jī)救拉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)瘫拣,“玉大人亿絮,你說(shuō)我怎么就攤上這事◆镏簦” “怎么了派昧?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)拢切。 經(jīng)常有香客問(wèn)我蒂萎,道長(zhǎng),這世上最難降的妖魔是什么淮椰? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任五慈,我火速辦了婚禮,結(jié)果婚禮上主穗,老公的妹妹穿的比我還像新娘泻拦。我一直安慰自己,他們只是感情好黔牵,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布聪轿。 她就那樣靜靜地躺著猾浦,像睡著了一般陆错。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上音瓷,一...
    開(kāi)封第一講書(shū)人閱讀 51,301評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音绳慎,去河邊找鬼纵竖。 笑死,一個(gè)胖子當(dāng)著我的面吹牛杏愤,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播通殃,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼厕宗!你這毒婦竟也來(lái)了画舌?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤已慢,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后佑惠,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡乍丈,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年把将,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了忆矛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片察蹲。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡洽议,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出亚兄,到底是詐尸還是另有隱情采驻,我是刑警寧澤审胚,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布膳叨,位于F島的核電站洽洁,受9級(jí)特大地震影響菲嘴,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜龄坪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一健田、第九天 我趴在偏房一處隱蔽的房頂上張望城豁。 院中可真熱鬧抄课,春花似錦、人聲如沸跟磨。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)僵蛛。三九已至尚蝌,卻和暖如春充尉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背驼侠。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留苛预,地道東北人笋熬。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親芜繁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容