2013-07-24 11 views
21

私は反射を使用してRows.Scan()関数を呼び出すことを検討しています。しかし、これにはさまざまなポインタが必要ですが、私はGolangには新しく、ソースの例はあまりありません。私はリフレクションを使用する必要があります。なぜなら、クエリ呼び出しの値をスライスに埋め込むことを計画しているからです。したがって、基本的にrows.Columns()を使用して行の長さを取得し、make()スライスを[]interface{}とし、通常はScan()関数に渡されたポインタを使用して埋められるデータポイントを埋めます。反射を使用してGolangでスキャンバリデーション機能を呼び出す方法は?

基本的にこのようなコード:

col := rows.Columns() 
vals := make([]interface{}, len(cols)) 
rows.Scan(&vals) 

誰もが、私は見てみることができリフレクションを使用して、ポインタを取る可変引数関数を呼び出すの例がありますか?

編集: 私が何をしているかわからないサンプルコード。

package main 

import (
    _ "github.com/lib/pq" 
    "database/sql" 
    "fmt" 
) 


func main() { 

    db, _ := sql.Open(
     "postgres", 
     "user=postgres dbname=Go_Testing password=ssap sslmode=disable") 

    rows, _ := db.Query("SELECT * FROM _users;") 

    cols, _ := rows.Columns() 

    for rows.Next() { 

     data := make([]interface{}, len(cols)) 

     rows.Scan(data...) 

     fmt.Println(data) 
    } 

} 

結果:

[<nil> <nil> <nil> <nil> <nil>] 
[<nil> <nil> <nil> <nil> <nil>] 
[<nil> <nil> <nil> <nil> <nil>] 
[<nil> <nil> <nil> <nil> <nil>] 
[<nil> <nil> <nil> <nil> <nil>] 
[<nil> <nil> <nil> <nil> <nil>] 

答えて

38

ここに私が到着した解決策があります。データをトラバースする前にタイプを取得していないので、値をScan()から抜き出す前に各タイプのタイプを手前に知っていませんが、実際にはそのタイプを知る必要はありません。

トリックは2つのスライスを作成しました.1つは値用で、もう1つは値スライスと平行にポインタを保持します。その後、ポインタを使用してデータを埋めると、値配列は実際にデータで満たされ、それを使用して他のデータ構造を埋めることができます。 lucidquietする

package main 

import (
    "fmt" 
    _ "github.com/lib/pq" 
    "database/sql" 
) 

func main() { 

    db, _ := sql.Open(
     "postgres", 
     "user=postgres dbname=go_testing password=pass sslmode=disable") 

    rows, _ := db.Query("SELECT * FROM _user;") 

    columns, _ := rows.Columns() 
    count := len(columns) 
    values := make([]interface{}, count) 
    valuePtrs := make([]interface{}, count) 

    for rows.Next() { 

     for i, _ := range columns { 
      valuePtrs[i] = &values[i] 
     } 

     rows.Scan(valuePtrs...) 

     for i, col := range columns { 

      var v interface{} 

      val := values[i] 

      b, ok := val.([]byte) 

      if (ok) { 
       v = string(b) 
      } else { 
       v = val 
      } 

      fmt.Println(col, v) 
     } 
    } 
} 
+0

クラウド内のデータベースを照会するためのコマンドライン端末の作成。これは出発点としてうまくいきました...ありがとう! – openwonk

+0

誰もが1.4.2以降でこれを試しましたか?私は文字列型のすべてのフィールドを取得しています – Anuruddha

+0

ちょうど私が必要なもの、私はこのコミュニティが大好きです – Mauricio

4

は、私はあなたがこのために反射を必要とは思わない - あなたは可変引数関数に複数の値を渡すために、スライスと...演算子を使用することができます。

col := rows.Columns() 
vals := make([]interface{}, col) 
rows.Scan(vals...) 

私はあなたがしたいことを誤解している可能性があります。

+0

これはうまくいくようです。私はサンプルコードで質問を更新しました。このコードで '...'演算子を使うと、 '無効なメモリアドレスまたはnilポインタ逆参照 'が生成されます。私はポインタや何かでデータ配列を初期化する必要があるように感じる... – lucidquiet

+0

メモリアドレスの問題は私のSQLであり、オペレータの使用ではなかった。私はコンパイルしてコードを修正プログラムで実行することができますが、データは取得されません。データ(またはサンプルのval)のすべてのエントリはゼロです。だから、最終的にこれは私が望むようにはうまくいかなかった。 – lucidquiet

+3

-1: 'Rows.Columns()'は '([文字列、エラー)'を返します。ポインタを渡さないと 'Scan()'が失敗します(そして '[] interface {}'はポインタの配列ではないことを不平にします) )。 – weberc2

1

:あなたはまた、代わりにスライス

次のコードは、良い作品を作るのインターフェースを割り当てることができます。

var sql = "select * from table" 
rows, err := db.Query(sql) 
columns, err = rows.Columns() 
colNum := len(columns) 

var values = make([]interface{}, colNum) 
for i, _ := range values { 
    var ii interface{} 
    values[i] = &ii 
} 

for rows.Next() { 
    err := rows.Scan(values...) 
    for i, colName := range columns { 
     var raw_value = *(values[i].(*interface{})) 
     var raw_type = reflect.TypeOf(raw_value) 

     fmt.Println(colName,raw_type,raw_value) 
    } 
} 
1

次のソリューションを使用して、フィールドでフィールドを参照することができますインデックスの代わりに名前。これは、より多くの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:[email protected](localhost:3306)/MyDB") 
     //db, err = sql.Open("mysql", "MyUser:[email protected](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) 
} 

出力例:

#がメインの実行行きます。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" 
} 
] 
+0

これはOPの質問に返信した場合私は確信していませんが、私はあなたの時間SQLデータベースから行データを取得してJSON化する方法についてのWeb ... Goでは簡単ですが、どのように知っておく必要があります!だから、ありがとう! :) –

関連する問題