SQLX 库

sqlx 是 Go 的软件包,它在出色的内置 database/sql 软件包的基础上提供了一组扩展。
该库兼容sql原生包,同时又提供了更为强大的、优雅的查询、插入函数。
该库提供四个处理类型,分别是:

  • sqlx.DB – 类似原生的 sql.DB;
  • sqlx.Tx – 类似原生的 sql.Tx;
  • sqlx.Stmt – 类似原生的 sql.Stmt, 准备 SQL 语句操作;
  • sqlx.NamedStmt – 对特定参数命名并绑定生成 SQL 语句操作。

提供两个游标类型,分别是:

  • sqlx.Rows – 类似原生的 sql.Rows, 从 Queryx 返回;
  • sqlx.Row – 类似原生的 sql.Row, 从 QueryRowx 返回。

  • 可以将Rows内容解析至struct(支持内嵌)、map、slice

  • 命名参数支持
  • Get/Select可以快速将查询结果转为为struct/slice

    handle类型

    sqlx最大可能去实现database/sql一样的功能。有4中主要handle类型:
  1. sqlx.DB - 相当于database/sql中的sql.DB,代表一个数据库。
  2. sqlx.Tx - 相当于database/sql中的sql.Tx,代表一个事务。
  3. sqlx.Stmt = 相当于database/sql中的sql.Stmt,代表一条要执行的语句。
  4. sqlx.NamedStmt - 代表一条有参数的执行语句。

所有handle类型内嵌实现了对应database/sql中handle。也就是说,当你在代码中调用sqlx.DB.Query的时候,同时也会调用执行到sql.DB.Query对应的代码。
除此之外,还有两种指针类型:

  • sqlx.Rows - 相当于sql.Rows, 从Queryx返回的指针;
  • sqlx.Row - 相当于sql.Row, 从QueryRowx返回的结果;

跟handle类型一样,sqlx.Rows内嵌了sql.Rows。犹豫sql.Row的底层实现未公开,sqlx.Row只是实现了sql.Row的部分标准接口。

bindvars

查询占位符?在内部称为bindvars(查询占位符),它非常重要。你应该始终使用它们向数据库发送值,因为它们可以防止SQL注入攻击。database/sql不尝试对查询文本进行任何验证;它与编码的参数一起按原样发送到服务器。除非驱动程序实现一个特殊的接口,否则在执行之前,查询是在服务器上准备的。因此bindvars是特定于数据库的:

  • MySQL中使用 ?
  • PostgreSQL使用枚举的$1、$2等bindvar语法
  • SQLite中?和$1的语法都支持
  • Oracle中使用:name的语法

bindvars的一个常见误解是,它们用来在sql语句中插入值。它们其实仅用于参数化,不允许更改SQL语句的结构。例如,使用bindvars尝试参数化列或表名将不起作用:

  1. // ?不能用来插入表名(做SQL语句中表名的占位符)
  2. db.Query("SELECT * FROM ?", "mytable")
  3. // ?也不能用来插入列名(做SQL语句中列名的占位符)
  4. db.Query("SELECT ?, ? FROM people", "name", "location")

安装与使用

安装 SQLX 库

  1. go get github.com/jmoiron/sqlx
  2. go get github.com/go-sql-driver/mysql # mysql驱动包

使用操作

Open/Connect

  • Open可能仅校验参数,而没有与db间创建连接,要确认db是否可用,需要调用Ping
  • Connect则相当于Open+Ping

使用如下:

  1. db, err := sqlx.Open("postgres", "user=foo dbname=bar sslmode=disable")
  2. if err != nil {
  3. log.Fatalln(err)
  4. }
  5. db, err := sqlx.Connect("mysql", "user:password@host:port?database")
  6. if err != nil {
  7. log.Fatalln(err)
  8. }
  1. // 初始化数据库
  2. func initMySQL() (err error) {
  3. dsn := "root:password@tcp(127.0.0.1:3306)/database"
  4. db, err = sqlx.Open("mysql", dsn)
  5. if err != nil {
  6. fmt.Printf("connect server failed, err:%v\n", err)
  7. return
  8. }
  9. db.SetMaxOpenConns(200) // 最大连接数
  10. db.SetMaxIdleConns(10) // 最大空闲数
  11. return db.Ping()
  12. }

Get/Select

Get和Select是一个非常省时的扩展,可直接将结果赋值给结构体,其内部封装了StructScan进行转化。

  • Get用于查询单条数据
  • Select则用于查询多条数据

需要注意的是方法中的dest必须满足要求,Get中不能为nil,Select中必须为slice。

  1. // 查询单条数据示例 结构体,Get
  2. func getDemo() {
  3. sqlStr := "select * from user where id=?"
  4. var u User
  5. err := db.Get(&u, sqlStr, 2)
  6. if err != nil {
  7. fmt.Printf("get failed err:%v\n", err)
  8. return
  9. }
  10. fmt.Println(u.ID, u.Name, u.Age)
  11. }
  12. // select查询
  13. func selectDemo() {
  14. sqlStr := "select * from user where id > ?"
  15. var users []User
  16. err := db.Select(&users, sqlStr, 0)
  17. if err != nil {
  18. fmt.Printf("get failed err:%v\n", err)
  19. return
  20. }
  21. fmt.Printf("users:%#v\n", users)
  22. }

tag注意

使用struct需要注意,sqlx默认解析的 tagdb,未设置tag,默认情况下是直接将field名转换为小写,因此默认情况下不满足需求时,需要注意设置field的tag名,否则可能因为不匹配而导致数据处理失败。

  1. type Person struct {
  2. FirstName string `db:"first_name"`
  3. LastName string `db:"last_name"`
  4. Email string
  5. }
  6. people := []Person{}
  7. db.Select(&people, "SELECT * FROM person ORDER BY first_name ASC")
  8. jason, john := people[0], people[1]
  9. jason = Person{}
  10. err = db.Get(&jason, "SELECT * FROM person WHERE first_name=$1", "Jason")
  11. fmt.Printf("%#v\n", jason)


如果存在null的情况,则field的类型必须设置为对应的sql.Null*类型(sql.NullBool、sql.NullInt64、sql.NullString等)

  1. type Place struct {
  2. Country string
  3. City sql.NullString
  4. TelCode int
  5. }
  6. // if you have null fields and use SELECT *, you must use sql.Null* in your struct
  7. places := []Place{}
  8. err = db.Select(&places, "SELECT * FROM place ORDER BY telcode ASC")
  9. if err != nil {
  10. fmt.Println(err)
  11. return
  12. }
  13. usa, singsing, honkers := places[0], places[1], places[2]
  14. fmt.Printf("%#v\n%#v\n%#v\n", usa, singsing, honkers)


Query

Query是database/sql中执行查询主要使用的方法,该方法返回row结果。
Query返回一个sql.Rows对象和一个error对象。

在使用的时候应该吧Rows当成一个游标而不是一系列的结果。尽管数据库驱动缓存的方法不一样,通过Next()迭代每次获取一列结果,对于查询结果非常巨大的情况下,可以有效的限制内存的使用,Scan()利用reflect把sql每一列结果映射到go语言的数据类型如string,[]byte等。如果你没有遍历完全部的rows结果,一定要记得在把connection返回到连接池之前调用rows.Close()。

Query返回的error有可能是在server准备查询的时候发生的,也有可能是在执行查询语句的时候发生的。例如可能从连接池中获取一个坏的连级(尽管数据库会尝试10次去发现或创建一个工作连接)。一般来说,错误主要由错误的sql语句,错误的类似匹配,错误的域名或表名等。

在大部分情况下,Rows.Scan()会把从驱动获取的数据进行拷贝,无论驱动如何使用缓存。特殊类型sql.RawBytes可以用来从驱动返回的数据总获取一个zero-copy的slice byte。当下一次调用Next的时候,这个值就不在有效了,因为它指向的内存已经被驱动重写了别的数据。

Query使用的connection在所有的rows通过Next()遍历完后或者调用rows.Close()后释放。

Queryx

Queryx和Query行为很相似,不过返回一个sqlx.Rows对象,支持扩展的scan行为,同时可将对数据进行结构体转换。

QueryRow和QueryRowx

QueryRow和QueryRowx都是从数据库中获取一条数据,但是QueryRowx提供scan扩展,可直接将结果转换为结构体。

  1. // Query使用的connection在所有的rows通过Next()遍历完后或者调用rows.Close()后释放
  2. func queryDemo() {
  3. rows, err := db.Query("select name, age from user")
  4. if err != nil {
  5. fmt.Println("query failed, error: ", err)
  6. return
  7. }
  8. // //循环结果
  9. for rows.Next() {
  10. var name string
  11. var age int
  12. err = rows.Scan(&name, &age)
  13. fmt.Println(name, age)
  14. }
  15. }
  16. // Queryx和Query行为很相似,不过返回一个sqlx.Rows对象
  17. //支持扩展的scan行为,同时可将对数据进行结构体转换
  18. func queryxDemo() {
  19. rows, err := db.Queryx("select id, name, age from user")
  20. if err != nil {
  21. fmt.Println("queryx failed, error: ", err)
  22. return
  23. }
  24. defer rows.Close()
  25. // //循环结果
  26. for rows.Next() {
  27. var u User
  28. err = rows.StructScan(&u)
  29. fmt.Println(u)
  30. }
  31. }
  32. //QueryRow和QueryRowx
  33. //QueryRow和QueryRowx都是从数据库中获取一条数据,但是QueryRowx提供scan扩展,可直接将结果转换为结构体。
  34. func queryRowAndQueryRowx() {
  35. row := db.QueryRow("select id, name, age FROM user where id = ?",3) // QueryRow返回错误,错误通过Scan返回
  36. var id int
  37. var name string
  38. var age int
  39. err :=row.Scan(&id,&name,&age)
  40. if err != nil{
  41. fmt.Println(err)
  42. }
  43. fmt.Printf("this is QueryRow res:[%d:%s:%d]\n",id,name,age)
  44. var u User
  45. err = db.QueryRowx("select id, name, age FROM user where id = ?",2).StructScan(&u)
  46. if err != nil{
  47. fmt.Println("QueryRowx error :",err)
  48. }else {
  49. fmt.Printf("this is QueryRowx res:%v",u)
  50. }
  51. }


Exec

Exec 和MustExec 从数据库连接池获取一个连接,然后在服务器上执行对应的sql语句。对于不支持即时查询(ad-hoc query)的数据库驱动,系统会自动新建一个预定义语句(prepared statement)然后执行对应sql操作。结果返回以后立即释放连接到连接池。

  1. schema := `CREATE TABLE place (
  2. country text,
  3. city text NULL,
  4. telcode integer);`
  5. // 直接执行sql操作
  6. result, err := db.Exec(schema)
  7. // 或者,通过MustExec执行,错误时抛出panic异常
  8. cityState := `INSERT INTO place (country, telcode) VALUES (?, ?)`
  9. countryCity := `INSERT INTO place (country, city, telcode) VALUES (?, ?, ?)`
  10. db.MustExec(cityState, "Hong Kong", 852)
  11. db.MustExec(cityState, "Singapore", 65)
  12. db.MustExec(countryCity, "South Africa", "Johannesburg", 27)

Exec执行sql语句而不返回rows,主要用于insert、update、delete。
返回结果result依赖于不同的驱动,一般包括两部分数据:LastInsertedId() 或 RowsAffected()。

例如,

  • MySQL中,可以通过 LastInsertedId() 在插入操作后直接获取自增以后的关键值;
  • PostgreSQL中,只能通过标准的 RETURNING 语句来获取。
  1. // Exec
  2. func execInsert() {
  3. sqlStr := "insert into user (name, age) values(?, ?)"
  4. ret, err := db.Exec(sqlStr, "Jerry", 18)
  5. if err != nil {
  6. fmt.Printf("insert failed, err:%v\n", err)
  7. return
  8. }
  9. theId, err := ret.LastInsertId()
  10. if err != nil {
  11. fmt.Println(err)
  12. }
  13. fmt.Println(theId)
  14. }
  15. func execUpdate() {
  16. sqlStr := "update user set age=? where id = ?"
  17. ret, err := db.Exec(sqlStr, 22, 1)
  18. if err != nil {
  19. fmt.Printf("update failed, err:%v\n", err)
  20. return
  21. }
  22. // 影响的行数
  23. count, err := ret.RowsAffected()
  24. if err != nil {
  25. fmt.Println(err)
  26. }
  27. fmt.Println(count)
  28. }
  29. func execDelete() {
  30. sqlStr := "delete from user where id=?"
  31. ret, err := db.Exec(sqlStr, 1)
  32. if err != nil {
  33. fmt.Println(err)
  34. }
  35. count, err := ret.RowsAffected()
  36. if err != nil {
  37. fmt.Println(err)
  38. }
  39. fmt.Println(count)
  40. }

开启事务

对应database/sql有Beginx、Preparex、Stmtx,带有Must前缀的方法,若发生错误则会panic

  1. tx := db.MustBegin()
  2. tx.MustExec("INSERT INTO person (first_name, last_name, email) VALUES ($1, $2, $3)", "Jason", "Moiron", "jmoiron@jmoiron.net")
  3. tx.MustExec("INSERT INTO person (first_name, last_name, email) VALUES ($1, $2, $3)", "John", "Doe", "johndoeDNE@gmail.net")
  4. tx.MustExec("INSERT INTO place (country, city, telcode) VALUES ($1, $2, $3)", "United States", "New York", "1")
  5. tx.MustExec("INSERT INTO place (country, telcode) VALUES ($1, $2)", "Hong Kong", "852")
  6. tx.MustExec("INSERT INTO place (country, telcode) VALUES ($1, $2)", "Singapore", "65")
  7. // 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
  8. tx.NamedExec("INSERT INTO person (first_name, last_name, email) VALUES (:first_name, :last_name, :email)", &Person{"Jane", "Citizen", "jane.citzen@example.com"})
  9. tx.Commit()


命名参数支持


支持对sql中的命名参数解析支持,命名参数的格式为:name,执行时会主动获取对应name的值。

  1. // Named queries, using `:name` as the bindvar. Automatic bindvar support
  2. // which takes into account the dbtype based on the driverName on sqlx.Open/Connect
  3. _, err = db.NamedExec(`INSERT INTO person (first_name,last_name,email) VALUES (:first,:last,:email)`,
  4. map[string]interface{}{
  5. "first": "Bin",
  6. "last": "Smuth",
  7. "email": "bensmith@allblacks.nz",
  8. })
  9. // Selects Mr. Smith from the database
  10. rows, err = db.NamedQuery(`SELECT * FROM person WHERE first_name=:fn`, map[string]interface{}{"fn": "Bin"})
  11. // Named queries can also use structs. Their bind names follow the same rules
  12. // as the name -> db mapping, so struct fields are lowercased and the `db` tag
  13. // is taken into consideration.
  14. rows, err = db.NamedQuery(`SELECT * FROM person WHERE first_name=:first_name`, jason)

sqlx.In

sqlx.In是sqlx提供的一个非常方便的函数。

sqlx.In批量插入

  1. // User 结构体实现driver.Valuer接口:
  2. func (u User) Value() (driver.Value, error) {
  3. return []interface{}{u.Name, u.Age}, nil
  4. }
  5. // BatchInsertUsers2 使用sqlx.In帮我们拼接语句和参数, 注意传入的参数是[]interface{}
  6. func BatchInsertUsers2(users []interface{}) error {
  7. query, args, _ := sqlx.In(
  8. "INSERT INTO user (name, age) VALUES (?), (?), (?)",
  9. users..., // 如果arg实现了 driver.Valuer, sqlx.In 会通过调用 Value()来展开它
  10. )
  11. fmt.Println(query) // 查看生成的querystring // INSERT INTO user (name, age) VALUES (?, ?), (?, ?), (?, ?)
  12. fmt.Println(args) // 查看生成的args // [西瓜 18 yy 23 kk 24]
  13. _, err := db.Exec(query, args...)
  14. return err
  15. }
  16. func testBatchInsertUsers2() {
  17. u1 := User{Name: "XX", Age: 18}
  18. u2 := User{Name: "ZZ", Age: 23}
  19. u3 := User{Name: "JJ", Age: 24}
  20. users := []interface{}{u1, u2, u3}
  21. err := BatchInsertUsers2(users)
  22. if err != nil {
  23. fmt.Printf("BatchInsertUsers2 failed, err:%v\n", err)
  24. }
  25. }
  26. // BatchInsertUsers3 使用NamedExec实现批量插入
  27. func BatchInsertUsers3(users []*User) error {
  28. _, err := db.NamedExec("INSERT INTO user (name, age) VALUES (:name, :age)", users)
  29. return err
  30. }
  31. func testBatchInsertUsers3() {
  32. u1 := User{Name: "西瓜", Age: 18}
  33. u2 := User{Name: "yy", Age: 23}
  34. u3 := User{Name: "kk", Age: 24}
  35. users := []*User{&u1, &u2, &u3}
  36. err := BatchInsertUsers3(users)
  37. if err != nil {
  38. fmt.Printf("BatchInsertUsers3 failed, err:%v\n", err)
  39. }
  40. }

sqlx.In查询

sqlx查询语句中实现In查询和FIND_IN_SET函数。即实现

  • SELEC * FROM user WHERE id in (3, 2, 1);
  • SELECT * FROM user WHERE id in (3, 2, 1) ORDER BY FIND_IN_SET(id, ‘3,2,1’);

区别:

  • in查询, 无法自定义顺序,默认id从小到大顺序
  • FIND_IN_SET函数(给定顺序)


查询id在给定id集合的数据并维持给定id集合的顺序。

  1. // 无法自定义顺序,默认id从小到大顺序
  2. func queryByIDs(ids []int) (users []User, err error) {
  3. query , args, err := sqlx.In("select * from user where id in (?)", ids)
  4. if err != nil {
  5. fmt.Println(err)
  6. return
  7. }
  8. // sqlx.In 返回带 `?` bindvar的查询语句, 我们使用Rebind()重新绑定它
  9. query = db.Rebind(query)
  10. err = db.Select(&users, query, args...)
  11. return
  12. }
  13. func testQueryByIDs() {
  14. // 无法自定义顺序,默认id从小到大顺序
  15. users, err := queryByIDs([]int {7, 3, 6, 2})
  16. if err != nil {
  17. fmt.Printf("QueryByIDs failed, err:%v\n", err)
  18. return
  19. }
  20. for _, user := range users {
  21. fmt.Printf("user:%#v\n", user)
  22. }
  23. /**
  24. user:main.User{ID:2, Age:20, Name:"tom"}
  25. user:main.User{ID:3, Age:19, Name:"jack"}
  26. user:main.User{ID:6, Age:18, Name:"Jerry"}
  27. user:main.User{ID:7, Age:18, Name:"Jerry"}
  28. */
  29. }
  30. // QueryAndOrderByIDs 按照指定id查询并维护顺序
  31. func QueryAndOrderByIDs(ids []int) (users []User, err error) {
  32. // 动态填充id
  33. strIDs := make([]string, 0, len(ids))
  34. for _, id := range ids {
  35. strIDs = append(strIDs, fmt.Sprintf("%d", id))
  36. }
  37. // FIND_IN_SET维护顺序
  38. // mysql中find_in_set()函数的使用及in()用法详解 https://www.jb51.net/article/143105.htm
  39. query, args, err := sqlx.In("SELECT * FROM user WHERE id IN (?) ORDER BY FIND_IN_SET(id, ?)", ids, strings.Join(strIDs, ","))
  40. if err != nil {
  41. return
  42. }
  43. // sqlx.In 返回带 `?` bindvar的查询语句, 我们使用Rebind()重新绑定它
  44. query = db.Rebind(query)
  45. err = db.Select(&users, query, args...)
  46. return
  47. }
  48. func testQueryAndOrderByIDs() {
  49. // 1. 用代码去做排序
  50. // 2. 让MySQL排序
  51. fmt.Println("----")
  52. users, err := QueryAndOrderByIDs([]int{2, 6, 3, 4}) // 维护id查询顺序
  53. if err != nil {
  54. fmt.Printf("QueryAndOrderByIDs failed, err:%v\n", err)
  55. return
  56. }
  57. for _, user := range users {
  58. fmt.Printf("user:%#v\n", user)
  59. }
  60. /**
  61. user:main.User{ID:2, Age:20, Name:"tom"}
  62. user:main.User{ID:6, Age:18, Name:"Jerry"}
  63. user:main.User{ID:3, Age:19, Name:"jack"}
  64. user:main.User{ID:4, Age:18, Name:"Jerry"}
  65. */
  66. }

全部代码

  1. package main
  2. import (
  3. "database/sql/driver"
  4. "fmt"
  5. _ "github.com/go-sql-driver/mysql" // 注册MySQL驱动
  6. "github.com/jmoiron/sqlx"
  7. "strings"
  8. )
  9. var db *sqlx.DB
  10. func initDB() (err error) {
  11. dsn := "root:Abcdef@123456@tcp(127.0.0.1:3306)/ueumd_test?charset=utf8mb4&parseTime=True"
  12. // Connect包含了open和ping方法,也可以使用MustConnect连接不成功就panic
  13. db, err = sqlx.Connect("mysql", dsn)
  14. if err != nil {
  15. fmt.Printf("connect DB failed, err:%v\n", err)
  16. return
  17. }
  18. db.SetMaxOpenConns(200)
  19. db.SetMaxIdleConns(10)
  20. return db.Ping()
  21. }
  22. type User struct {
  23. ID int `db:"id"`
  24. Age int `db:"age"`
  25. Name string `db:"name"`
  26. }
  27. //Get和Select是一个非常省时的扩展,可直接将结果赋值给结构体,其内部封装了StructScan进行转化。
  28. //Get用于获取单个结果然后Scan,Select用来获取结果切片。
  29. // 查询单条数据示例 结构体,Get
  30. func getDemo() {
  31. sqlStr := "select * from user where id=?"
  32. var u User
  33. err := db.Get(&u, sqlStr, 2)
  34. if err != nil {
  35. fmt.Printf("get failed err:%v\n", err)
  36. return
  37. }
  38. fmt.Println(u.ID, u.Name, u.Age)
  39. }
  40. // select查询
  41. func selectDemo() {
  42. sqlStr := "select * from user where id > ?"
  43. var users []User
  44. err := db.Select(&users, sqlStr, 0)
  45. if err != nil {
  46. fmt.Printf("get failed err:%v\n", err)
  47. return
  48. }
  49. fmt.Printf("users:%#v\n", users)
  50. }
  51. // Exec
  52. func execInsert() {
  53. sqlStr := "insert into user (name, age) values(?, ?)"
  54. ret, err := db.Exec(sqlStr, "Jerry", 18)
  55. if err != nil {
  56. fmt.Printf("insert failed, err:%v\n", err)
  57. return
  58. }
  59. theId, err := ret.LastInsertId()
  60. if err != nil {
  61. fmt.Println(err)
  62. }
  63. fmt.Println(theId)
  64. }
  65. func execUpdate() {
  66. sqlStr := "update user set age=? where id = ?"
  67. ret, err := db.Exec(sqlStr, 22, 1)
  68. if err != nil {
  69. fmt.Printf("update failed, err:%v\n", err)
  70. return
  71. }
  72. // 影响的行数
  73. count, err := ret.RowsAffected()
  74. if err != nil {
  75. fmt.Println(err)
  76. }
  77. fmt.Println(count)
  78. }
  79. func execDelete() {
  80. sqlStr := "delete from user where id=?"
  81. ret, err := db.Exec(sqlStr, 1)
  82. if err != nil {
  83. fmt.Println(err)
  84. }
  85. count, err := ret.RowsAffected()
  86. if err != nil {
  87. fmt.Println(err)
  88. }
  89. fmt.Println(count)
  90. }
  91. // Query使用的connection在所有的rows通过Next()遍历完后或者调用rows.Close()后释放
  92. func queryDemo() {
  93. rows, err := db.Query("select name, age from user")
  94. if err != nil {
  95. fmt.Println("query failed, error: ", err)
  96. return
  97. }
  98. // //循环结果
  99. for rows.Next() {
  100. var name string
  101. var age int
  102. err = rows.Scan(&name, &age)
  103. fmt.Println(name, age)
  104. }
  105. }
  106. // Queryx和Query行为很相似,不过返回一个sqlx.Rows对象
  107. //支持扩展的scan行为,同时可将对数据进行结构体转换
  108. func queryxDemo() {
  109. rows, err := db.Queryx("select id, name, age from user")
  110. if err != nil {
  111. fmt.Println("queryx failed, error: ", err)
  112. return
  113. }
  114. defer rows.Close()
  115. // //循环结果
  116. for rows.Next() {
  117. var u User
  118. err = rows.StructScan(&u)
  119. fmt.Println(u)
  120. }
  121. }
  122. //QueryRow和QueryRowx
  123. //QueryRow和QueryRowx都是从数据库中获取一条数据,但是QueryRowx提供scan扩展,可直接将结果转换为结构体。
  124. func queryRowAndQueryRowx() {
  125. row := db.QueryRow("select id, name, age FROM user where id = ?",3) // QueryRow返回错误,错误通过Scan返回
  126. var id int
  127. var name string
  128. var age int
  129. err :=row.Scan(&id,&name,&age)
  130. if err != nil{
  131. fmt.Println(err)
  132. }
  133. fmt.Printf("this is QueryRow res:[%d:%s:%d]\n",id,name,age)
  134. var u User
  135. err = db.QueryRowx("select id, name, age FROM user where id = ?",2).StructScan(&u)
  136. if err != nil{
  137. fmt.Println("QueryRowx error :",err)
  138. }else {
  139. fmt.Printf("this is QueryRowx res:%v",u)
  140. }
  141. }
  142. // NamedQuery
  143. //NamedQuery方法用来绑定SQL语句与结构体或map中的同名字段, 查询。
  144. // Sqlx.In
  145. func queryByIDs(ids []int) (users []User, err error) {
  146. query , args, err := sqlx.In("select * from user where id in (?)", ids)
  147. if err != nil {
  148. fmt.Println(err)
  149. return
  150. }
  151. // sqlx.In 返回带 `?` bindvar的查询语句, 我们使用Rebind()重新绑定它
  152. query = db.Rebind(query)
  153. err = db.Select(&users, query, args...)
  154. return
  155. }
  156. func testQueryByIDs() {
  157. // 无法自定义顺序,默认id从小到大顺序
  158. users, err := queryByIDs([]int {7, 3, 6, 2})
  159. if err != nil {
  160. fmt.Printf("QueryByIDs failed, err:%v\n", err)
  161. return
  162. }
  163. for _, user := range users {
  164. fmt.Printf("user:%#v\n", user)
  165. }
  166. /**
  167. user:main.User{ID:2, Age:20, Name:"tom"}
  168. user:main.User{ID:3, Age:19, Name:"jack"}
  169. user:main.User{ID:6, Age:18, Name:"Jerry"}
  170. user:main.User{ID:7, Age:18, Name:"Jerry"}
  171. */
  172. }
  173. // QueryAndOrderByIDs 按照指定id查询并维护顺序
  174. func QueryAndOrderByIDs(ids []int) (users []User, err error) {
  175. // 动态填充id
  176. strIDs := make([]string, 0, len(ids))
  177. for _, id := range ids {
  178. strIDs = append(strIDs, fmt.Sprintf("%d", id))
  179. }
  180. // FIND_IN_SET维护顺序
  181. // mysql中find_in_set()函数的使用及in()用法详解 https://www.jb51.net/article/143105.htm
  182. query, args, err := sqlx.In("SELECT * FROM user WHERE id IN (?) ORDER BY FIND_IN_SET(id, ?)", ids, strings.Join(strIDs, ","))
  183. if err != nil {
  184. return
  185. }
  186. // sqlx.In 返回带 `?` bindvar的查询语句, 我们使用Rebind()重新绑定它
  187. query = db.Rebind(query)
  188. err = db.Select(&users, query, args...)
  189. return
  190. }
  191. func testQueryAndOrderByIDs() {
  192. // 1. 用代码去做排序
  193. // 2. 让MySQL排序
  194. fmt.Println("----")
  195. users, err := QueryAndOrderByIDs([]int{2, 6, 3, 4}) // 维护id查询顺序
  196. if err != nil {
  197. fmt.Printf("QueryAndOrderByIDs failed, err:%v\n", err)
  198. return
  199. }
  200. for _, user := range users {
  201. fmt.Printf("user:%#v\n", user)
  202. }
  203. }
  204. // User 结构体实现driver.Valuer接口:
  205. func (u User) Value() (driver.Value, error) {
  206. return []interface{}{u.Name, u.Age}, nil
  207. }
  208. // BatchInsertUsers2 使用sqlx.In帮我们拼接语句和参数, 注意传入的参数是[]interface{}
  209. func BatchInsertUsers2(users []interface{}) error {
  210. query, args, _ := sqlx.In(
  211. "INSERT INTO user (name, age) VALUES (?), (?), (?)",
  212. users..., // 如果arg实现了 driver.Valuer, sqlx.In 会通过调用 Value()来展开它
  213. )
  214. fmt.Println(query) // 查看生成的querystring // INSERT INTO user (name, age) VALUES (?, ?), (?, ?), (?, ?)
  215. fmt.Println(args) // 查看生成的args // [西瓜 18 yy 23 kk 24]
  216. _, err := db.Exec(query, args...)
  217. return err
  218. }
  219. func testBatchInsertUsers2() {
  220. u1 := User{Name: "XX", Age: 18}
  221. u2 := User{Name: "ZZ", Age: 23}
  222. u3 := User{Name: "JJ", Age: 24}
  223. users := []interface{}{u1, u2, u3}
  224. err := BatchInsertUsers2(users)
  225. if err != nil {
  226. fmt.Printf("BatchInsertUsers2 failed, err:%v\n", err)
  227. }
  228. }
  229. // BatchInsertUsers3 使用NamedExec实现批量插入
  230. func BatchInsertUsers3(users []*User) error {
  231. _, err := db.NamedExec("INSERT INTO user (name, age) VALUES (:name, :age)", users)
  232. return err
  233. }
  234. func testBatchInsertUsers3() {
  235. u1 := User{Name: "西瓜", Age: 18}
  236. u2 := User{Name: "yy", Age: 23}
  237. u3 := User{Name: "kk", Age: 24}
  238. users := []*User{&u1, &u2, &u3}
  239. err := BatchInsertUsers3(users)
  240. if err != nil {
  241. fmt.Printf("BatchInsertUsers3 failed, err:%v\n", err)
  242. }
  243. }
  244. func main() {
  245. if err := initDB(); err != nil {
  246. fmt.Printf("init DB failed, err:%v\n", err)
  247. return
  248. }
  249. fmt.Println("init DB success...")
  250. //getDemo()
  251. //selectDemo()
  252. //execInsert()
  253. //execUpdate()
  254. //execDelete()
  255. // queryDemo()
  256. // queryxDemo()
  257. //queryRowAndQueryRowx()
  258. //testQueryByIDs()
  259. //
  260. //testQueryAndOrderByIDs()
  261. testBatchInsertUsers2()
  262. // testBatchInsertUsers3()
  263. }

参考连接