Friday, May 6, 2016

select data from database

The following solution allows you to refer to the field by field name instead of index. It's more like PHP style:

Table definition:

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)
}

Sample output:

# go run 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"
 }
]

No comments: