以下解决方案允许您通过字段名称而不是索引来引用字段。它更像是 PHP 风格:
表定义:
CREATE TABLE `salesOrder` (
`idOrder` int(10) unsigned NOT NULL AUTO_INCREMENT,
`uid` int(10) unsigned NOT NULL,
`changed` datetime NOT NULL,
PRIMARY KEY (`idOrder`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
main.go:
package main
import (
"database/sql"
"encoding/json"
"fmt"
_ "github.com/go-sql-driver/mysql"
"log"
"reflect"
"strings"
)
var (
db *sql.DB
)
func initDB() {
var err error
// The database/sql package manages the connection pooling automatically for you.
// sql.Open(..) returns a handle which represents a connection pool, not a single connection.
// The database/sql package automatically opens a new connection if all connections in the pool are busy.
// Reference: http://stackoverflow.com/questions/17376207/how-to-share-mysql-connection-between-http-goroutines
db, err = sql.Open("mysql", "MyUser:MyPassword@tcp(localhost:3306)/MyDB")
//db, err = sql.Open("mysql", "MyUser:MyPassword@tcp(localhost:3306)/MyDB?tx_isolation='READ-COMMITTED'") // optional
if err != nil {
log.Fatalf("Error on initializing database connection: %v", err.Error())
}
// Open doesn't open a connection. Validate DSN data:
err = db.Ping()
if err != nil {
log.Fatalf("Error on opening database connection: %v", err.Error())
}
}
func StrutToSliceOfFieldAddress(s interface{}) []interface{} {
fieldArr := reflect.ValueOf(s).Elem()
fieldAddrArr := make([]interface{}, fieldArr.NumField())
for i := 0; i < fieldArr.NumField(); i++ {
f := fieldArr.Field(i)
fieldAddrArr[i] = f.Addr().Interface()
}
return fieldAddrArr
}
func testSelectMultipleRowsV3(optArr map[string]interface{}) {
// queries
query := []string{}
param := []interface{}{}
if val, ok := optArr["idOrder"]; ok {
query = append(query, "salesOrder.idOrder >= ?")
param = append(param, val)
}
// The first character of the field name must be in upper case. Otherwise, you would get:
// panic: reflect.Value.Interface: cannot return value obtained from unexported field or method
var sqlField = struct {
IdOrder int
Uid int
Changed string
}{}
var rowArr []interface{}
sqlFieldArrPtr := StrutToSliceOfFieldAddress(&sqlField)
sql := "SELECT "
sql += " salesOrder.idOrder "
sql += ", salesOrder.uid "
sql += ", salesOrder.changed "
sql += "FROM salesOrder "
sql += "WHERE " + strings.Join(query, " AND ") + " "
sql += "ORDER BY salesOrder.idOrder "
stmt, err := db.Prepare(sql)
if err != nil {
log.Printf("Error: %v", err)
}
defer stmt.Close()
rows, err := stmt.Query(param...)
if err != nil {
log.Printf("Error: %v", err)
}
defer rows.Close()
if err != nil {
log.Printf("Error: %v", err)
}
//sqlFields, err := rows.Columns()
for rows.Next() {
err := rows.Scan(sqlFieldArrPtr...)
if err != nil {
log.Printf("Error: %v", err)
}
// Show the type of each struct field
f1 := reflect.TypeOf(sqlField.IdOrder)
f2 := reflect.TypeOf(sqlField.Uid)
f3 := reflect.TypeOf(sqlField.Changed)
fmt.Printf("Type: %v\t%v\t%v\n", f1, f2, f3)
// Show the value of each field
fmt.Printf("Row: %v\t%v\t%v\n\n", sqlField.IdOrder, sqlField.Uid, sqlField.Changed)
rowArr = append(rowArr, sqlField)
}
if err := rows.Err(); err != nil {
log.Printf("Error: %v", err)
}
// produces neatly indented output
if data, err := json.MarshalIndent(rowArr, "", " "); err != nil {
log.Fatalf("JSON marshaling failed: %s", err)
} else {
fmt.Printf("json.MarshalIndent:\n%s\n\n", data)
}
}
func main() {
initDB()
defer db.Close()
// this example shows how to dynamically assign a list of field name to the rows.Scan() function.
optArr := map[string]interface{}{}
optArr["idOrder"] = 1
testSelectMultipleRowsV3(optArr)
}
样本输出:
# 运行 main.go
Type: int int string
Row: 1 1 2016-05-06 20:41:06
Type: int int string
Row: 2 2 2016-05-06 20:41:35
json.MarshalIndent:
[
{
"IdOrder": 1,
"Uid": 1,
"Changed": "2016-05-06 20:41:06"
},
{
"IdOrder": 2,
"Uid": 2,
"Changed": "2016-05-06 20:41:35"
}
]