2013-07-24 13 views
21

Yansımayı kullanarak Rows.Scan() işlevini aramak için arıyorum. Ancak, değişken bir sayı işaretçisi alır, ancak Golang’da yeniyim ve pek çok kaynak örneği yok. Yansıma kullanmam gerekiyor çünkü ben bir Query çağrısından gelen değerlerle bir dilim doldurmayı planlıyorum. Bu nedenle, işlevini kullanarak satırın uzunluğunu elde edin ve make()[]interface{} bir dilim []interface{} bir dilim Scan() işlevine iletilen işaretçiler kullanılarak normal olarak doldurulacak veri noktalarıyla doldurun.Yansıma kullanarak Golang'daki Scan variadic işlevini çağırmak nasıl?

Temelde bu kodu gibi bir şey:

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

Herkes ben bakmak olabilir yansıma kullanarak işaretçileri alır variadic işlevini çağırarak bir örnek var mı?

Düzelt: Kullandığım şeyi yapma gibi görünmeyen örnek kod.

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

} 

sonuçları: - Eğer variadic işleve birden çok değer geçirmek için bir dilim ve ... operatörünü kullanabilirsiniz

[<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>] 

cevap

38

İşte geldiğim çözüm. Verileri geçmeden önce Türleri alamaz ve bu nedenle değerleri Scan() aracılığıyla çekmeden önce her bir değerin türünü bilmeden bilmezsiniz, ancak bu nokta, elden önce türleri bilmek zorunda değildir.

Hile, değerler için bir tane olmak üzere 2 dilim ve değerler dilimine paralel olarak işaretçileri tutan bir dil oluşturmaktı. Daha sonra, işaretler veriyi doldurmak için kullanıldıktan sonra, değerler dizisi aslında verilerle doldurulur ve bu da diğer veri yapılarını doldurmak için kullanılabilir.

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

Buluttaki veritabanlarını sorgulamak için bir komut satırı terminali oluşturma. Bu güzel bir başlangıç ​​noktası olarak çalıştı ... Teşekkürler! – openwonk

+0

Bunu herkes denedi 1.4.2 veya daha yüksek? – Anuruddha

+0

dize türündeki tüm alanları alıyorum Sadece ihtiyacım olan şey, bu topluluğa bayılıyorum – Mauricio

4

bunun için size yansımasını gerek olduğunu sanmıyorum.

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

Ne yapmak istediğinizi yanlış anlayabilirim!

+0

Peki gidin görünüyor. Sorumu örnek kodla güncelledim. Bu kod ile '...' operatörünün kullanılması and geçersiz bellek adresi veya imleç göstergesidir. Veri dizisini işaretçilerle veya bir şeyle başlatmam gerekiyormuş gibi hissediyorum ... – lucidquiet

+0

Hafıza adresi sorununun, sql'mde olduğunu ve kullanıcı tarafından kullanılmadığını kazıyorum. Kodu şimdi düzeltmeyle derleyebilir ve çalıştırabilirim, ancak veri alamıyorum, verilere (veya örnek değerlerinize) her giriş sıfırdır. Yani, sonuçta bu ben istediğim gibi çalışmadı. – lucidquiet

+3

-1: 'Rows.Columns()' döner '([] dize, hata)'; Ayrıca, Scan() - işaretçilerden geçmezseniz başarısız olur ve (a) kabul edilen bir yanıtı arabirimlere gösterici olarak değerlerini ilklendirmedikçe, [[] arabirimi {} 'nin bir dizi işaretçi olmadığını şikayet eder.). – weberc2

1

lucidquiet için:

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

aşağıdaki çözümü alana göre alanına bakın sağlar: Ayrıca

Aşağıdaki kod iyi çalışan bir dilim yapma arayüzü yerine atayabilirsiniz dizin yerine isim. Daha PHP tarzı gibi:

Tablo tanım:

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

Örnek çıktı:

# ana çalıştırmak gidin.o iş olur gibi

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

Bunun OP sorusuna cevap verip vermediğinden emin değilim, ama saatlerce beni aradığınızı söylemeliyim. Bir SQL veritabanı ve JSONify satır verileri almak için nasıl Web ... Go Go çok kolay, ama nasıl bilmeniz gerekir! Yani, çok teşekkür ederim! :) –

İlgili konular