golang用pgx查詢數據時如何將查詢結果方便的放入Map中

pgx庫簡介

下面是來自官網的簡介: pgx - PostgreSQL驅動和工具包

pgx是一個用於PostgreSQL的純Go語言驅動和工具包。

pgx驅動是一個底層的高性能接口,暴露了PostgreSQL特有的功能,如LISTEN/NOTIFYCOPY。它還包含一個標準database/sql接口的適配器。

工具包組件是一組相關的包,實現了像解析wire協議和PostgreSQL與Go之間的類型映射等PostgreSQL功能。這些底層包可用於實現替代驅動、代理、負載均衡器、邏輯複製客戶端等。

pgx應該是目前最好的啓動包了,“pg” 另一個著名的PostgreSQL啓動包的狀態目前僅僅是維護了,並且推薦了pgx。

"對於需要新功能或可靠解決報告中的錯誤的用戶,我們推薦使用正在積極開發中的pgx。" -pq維護人員

pgx比較底層,沒有ORM的功能,比如我要將一行查詢結果掃描到一個Map中。

近期由於開發一個pg到elastic的複製工具(包括wal增量複製和全量複製),在實現全量複製時,我需要將一行查詢結果放到Map中,結果尷尬了,pgx中竟然沒有這樣的功能,難道我要因此使用gorm或者xorm(雖然也很方便,但是多引入一個包我也不那麼願意),經過詢問必應,只得到了,自己聲明一堆和字段對應變量然後掃描這樣的例子:

 
 // RetreiveBook
 func RetreiveBook(db *sql.DB) ([]Book, error) {
    var books []Book
    // 查詢
    sql := `select book_id, title, author, to_char(publish_date, 'YYYY/MM/DD') as publish_date from m_book`
    rows, err := db.Query(sql)
    if err != nil {
       log.Println(err)
       return books, err
    }
    defer rows.Close()
 
    for rows.Next() {
       var book Book
       // 獲取各列的值,放到對應的地址中
       rows.Scan(&book.BookId, &book.Title, &book.Author, &book.PublishDate)
       books = append(books, book)
    }
 
    return books, nil
 }
 

太麻煩了,我忍不了。

pgx有內置的方法

於是我不甘心的仔細查看pgx的文檔和實例,終於,我發現了這個方法:

 func pgx.CollectRows(rows pgx.Rows, fn pgx.RowToFunc[map[string]any]) ([]map[string]any, error)
 // func[T any](rows pgx.Rows, fn pgx.RowToFunc[T]) ([]T, error)
 // CollectRows iterates through rows, calling fn for each row, and collecting the results into a slice of T.

但是顯然,這個方法是一次性的生成一個map的切片,如果數據量不大還好,但是對於我要用到的全量複製場景就未免不適合了,但是隻要有了方向就好了,趕緊跟蹤進去,

 // CollectRows iterates through rows, calling fn for each row, and collecting the results into a slice of T.
 func CollectRows[T any](rows Rows, fn RowToFunc[T]) ([]T, error) {
  defer rows.Close()
 
  slice := []T{}
 
  for rows.Next() {
  value, err := fn(rows)
  if err != nil {
  return nil, err
  }
  slice = append(slice, value)
  }
 
  if err := rows.Err(); err != nil {
  return nil, err
  }
 
  return slice, nil
 }

可見上述代碼中,僅僅是簡單的Next() Scan()然後回調pgx.RowToMap這個函數生成map,我不需要它一次性地給我map的切片,但是我可以輕鬆將他實現的過程移植到我的代碼中,至此問題完美解決了。至於pgx.RowToMap這個方法的實現,其實非常簡單,下面就是其源代碼:

 // RowToMap returns a map scanned from row.
 func RowToMap(row CollectableRow) (map[string]any, error) {
  var value map[string]any
  err := row.Scan((*mapRowScanner)(&value))
  return value, err
 }
 
 type mapRowScanner map[string]any
 
 func (rs *mapRowScanner) ScanRow(rows Rows) error {
  values, err := rows.Values()
  if err != nil {
  return err
  }
 
  *rs = make(mapRowScanner, len(values))
 
  for i := range values {
  (*rs)[string(rows.FieldDescriptions()[i].Name)] = values[i]
  }
 
  return nil
 }

其中19行就是這個屬性方法rows.FieldDescriptions()我不知道,否則自己也可以方便地實現掃描到map的方法了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章