1 回答

TA貢獻1831條經驗 獲得超9個贊
差不多:沒有。
Query 方法將返回一個指向 Rows 結構的指針:
func (db *DB) Query(query string, args ...interface{}) (*Rows, error)
如果你打印那個 ( fmt.Printf("%#v\n", rows)) 你會看到這樣的東西:
&sql.Rows{dc:(*sql.driverConn)(0xc8201225a0), releaseConn:(func(error)(0x4802c0), rowsi:(*pq.rows)(0xc820166700), closed:false, lastcols:[]driver.Value(nil), lasterr:error(nil), closeStmt:driver.Stmt(nil)}
...可能不是你想要的。
這些對應于sql 包中的 Rows 結構(您會注意到字段未導出):
type Rows struct {
dc *driverConn // owned; must call releaseConn when closed to release
releaseConn func(error)
rowsi driver.Rows
closed bool
lastcols []driver.Value
lasterr error // non-nil only if closed is true
closeStmt driver.Stmt // if non-nil, statement to Close on close
}
你會看到 []driver.Value(來自驅動程序包的一個接口),看起來我們可以期望找到一些有用的,甚至是人類可讀的數據。但是當直接打印時它似乎沒有用,它甚至是空的......所以你必須以某種方式獲取底層信息。sql 包為我們提供了 Next 方法:
Next 準備下一個結果行以使用 Scan 方法讀取。成功時返回 true,如果沒有下一個結果行或在準備它時發生錯誤,則返回 false。應咨詢 Err 以區分這兩種情況。
對 Scan 的每次調用,即使是第一個調用,都必須在調用 Next 之前。
接下來將制作一個 []driver.Value 與我擁有的列數相同的大小,它可以通過 driver.Rows(rowsi 字段)訪問(在 sql 包中),并用查詢中的值填充它。
在調用 rows.Next() 之后,如果你做了同樣的事情,fmt.Printf("%#v\n", rows) 你現在應該看到 []diver.Value 不再是空的,但它仍然不是你可以閱讀的任何東西,更可能是類似的東西:[]diver.Value{[]uint8{0x47, 0x65...
而且由于該字段未導出,您甚至無法嘗試將其轉換為更有意義的內容。但是 sql 包為我們提供了一種對數據進行處理的方法,即 Scan。
Scan 方法非常簡潔,有冗長的注釋,我不會在此處粘貼,但真正重要的一點是它涵蓋了您從 Next 方法和調用獲得的當前行中的列convertAssign(dest[i], sv),您可以在此處看到: https ://golang.org/src/database/sql/convert.go
它很長但實際上相對簡單,它本質上是切換源和目標的類型,并在可能的地方進行轉換,然后從源復制到目標;函數注釋告訴我們:
convertAssign 將 src 中的值復制到 dest,如果可能,將其轉換。如果副本會導致信息丟失,則會返回錯誤。dest 應該是一個指針類型。
所以現在你有了一個方法 (Scan),你可以直接調用它,并返回轉換后的值。您上面的代碼示例很好(除了可能在 Scan 錯誤時調用 Fatal() )。
重要的是要意識到 sql 包必須與特定的驅動程序一起使用,而該驅動程序又是為特定的數據庫軟件實現的,因此在幕后進行了相當多的工作。
我認為如果你想隱藏/概括整個 Query() ---> Next() ---> Scan() 習慣用法,你最好的選擇是將它放到另一個在幕后執行的函數中......寫一個您在其中抽象出更高級別的實現的包,因為 sql 包抽象了一些特定于驅動程序的細節,轉換和復制,填充行等。
- 1 回答
- 0 關注
- 239 瀏覽
添加回答
舉報