Go使用database/sql
查詢資料庫時應總是defer Rows.Close()
確保關閉回傳的result set並釋放連線。
下面使用sql的DB.Query()
查詢資料庫並回傳rows, err
。先判斷是否有err
,然後才呼叫defer rows.Close()
。因為如果有err
則rows
為nil會導致panic。
package main
...
func main() {
db := connect() // get DB object
rows, err := db.Query("SELECT ... ")
if err != nil { // check if err exists
panic(err)
}
defer rows.Close() // then call defer rows.Close() to avoid runtime panic of invoking nil rows object
for rows.Next() { // iterate each row of result set
...
}
}
雖然Rows.Next()
遍歷result set到最後一筆後內部會呼叫Rows.Close()
關閉result set,但若因為某些原因在最後一筆前就停止則 Rows.Next()
內部不會觸發Rows.Close()
,因此明確地手動調用defer rows.Close()
以確保關閉result set並釋放連線。
節錄Rows.Next()
原始碼如下。
sql.go
// Next prepares the next result row for reading with the Scan method. It
// returns true on success, or false if there is no next result row or an error
// happened while preparing it. Err should be consulted to distinguish between
// the two cases.
//
// Every call to Scan, even the first one, must be preceded by a call to Next.
func (rs *Rows) Next() bool {
var doClose, ok bool
withLock(rs.closemu.RLocker(), func() {
doClose, ok = rs.nextLocked()
})
if doClose {
rs.Close()
}
return ok
}
func (rs *Rows) nextLocked() (doClose, ok bool) {
...
rs.lasterr = rs.rowsi.Next(rs.lastcols)
if rs.lasterr != nil {
// Close the connection if there is a driver error.
if rs.lasterr != io.EOF {
return true, false
}
nextResultSet, ok := rs.rowsi.(driver.RowsNextResultSet)
if !ok {
return true, false
}
// The driver is at the end of the current result set.
// Test to see if there is another result set after the current one.
// Only close Rows if there is no further result sets to read.
if !nextResultSet.HasNextResultSet() {
doClose = true
}
return doClose, false
}
...
}
沒有留言:
張貼留言