亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

如何在 Go DRY 中掃描數據庫行?

如何在 Go DRY 中掃描數據庫行?

Go
慕容森 2023-05-08 14:39:21
我在數據庫中有一個包含用戶帳戶信息的表。我有一個名為用戶定義的結構。type User struct {  Id        uint  Username  string  Password  string  FirstName string  LastName  string  Address1  string  Address2  string  .... a bunch more fields ...}為了獲取個人用戶帳戶,我定義了一個方法func (user *User) GetById(db *sql.DB, id uint) error {  query := `SELECT             ...a whole bunch of SQL ...            WHERE id = $1            ... more SQL ...            LIMIT 1`  row := db.QueryRow(query, id)  err := row.Scan(    &user.Id,    &user.UserName,    &user.Password,    &user.FirstName,    &user.LastName,    ... some 20 more lines of fields read into the struct ...  )  if err != nil {    return err  }  return nil}在系統中有幾個地方我需要獲取用戶信息作為更大查詢的一部分。也就是說,我正在獲取一些其他類型的對象,還有一個與之相關的用戶帳戶。這意味著,我必須rows.Scan(&user.Username, &user...)一遍又一遍地重復整個過程,這會占用整個頁面,而且很容易出錯,如果我更改了用戶表結構,我將不得不更改很多地方的代碼。我怎樣才能讓它更干燥?編輯:我不確定為什么將其標記為重復,但由于需要進行此編輯,我將嘗試再解釋一次。我不是在問如何將一行掃描到一個結構中。正如上面的代碼清楚地顯示的那樣,我已經知道該怎么做。我在問如何構建結構掃描代碼,這樣我就不必每次掃描相同類型的結構時都重復掃描代碼的同一頁。編輯:另外,是的,我知道 sqlstruct 和 sqlx 以及類似的庫。我故意避免這些,因為它們依賴于 reflect 包,并有詳細記錄的性能問題。我打算使用這些技術潛在地掃描數百萬行(不是數百萬用戶,但這個問題擴展到其他記錄類型)。編輯:所以,是的,我知道我應該寫一個函數。我不確定這個函數應該將什么作為參數以及它應該返回什么結果??梢哉f我想要容納的另一個查詢看起來像這樣SELECT    s.id,    s.name,    ... more site fields ...    u.id,    u.username,    ... more user fields ...FROM site AS sJOIN user AS u ON (u.id = s.user_id)JOIN some_other_table AS st1 ON (site.id = st1.site_id)... more SQL ...我有一個嵌入用戶結構的站點結構方法。這里不想重復用戶掃碼。我想調用一個函數,它將 raw 的用戶部分掃描到用戶結構中,就像在上面的用戶方法中一樣。
查看完整描述

1 回答

?
莫回無

TA貢獻1865條經驗 獲得超7個贊

為了消除掃描結構所需步驟的重復,*sql.Rows您可以引入兩個接口。一個描述*sql.Rows和的已經實現的行為*sql.Row。


// This interface is already implemented by *sql.Rows and *sql.Row.

type Row interface {

    Scan(...interface{}) error

}

另一個抽象出行的實際掃描步驟。


// have your entity types implement this one

type RowScanner interface {

    ScanRow(Row) error

}

RowScanner 接口的示例實現如下所示:


type User struct {

    Id       uint

    Username string

    // ...

}


// Implements RowScanner

func (u *User) ScanRow(r Row) error {

    return r.Scan(

        &u.Id,

        &u.Username,

        // ...

    )

}


type UserList struct {

    Items []*User

}


// Implements RowScanner

func (list *UserList) ScanRow(r Row) error {

    u := new(User)

    if err := u.ScanRow(r); err != nil {

        return err

    }

    list.Items = append(list.Items, u)

    return nil

}

使用這些接口,您現在可以為所有通過使用這兩個函數實現 RowScanner 接口的類型干燥行掃描代碼。


func queryRows(query string, rs RowScanner, params ...interface{}) error {

    rows, err := db.Query(query, params...)

    if err != nil {

        return err

    }

    defer rows.Close()


    for rows.Next() {

        if err := rs.ScanRow(rows); err != nil {

            return err

        }

    }

    return rows.Err()

}


func queryRow(query string, rs RowScanner, params ...interface{}) error {

    return rs.ScanRow(db.QueryRow(query, params...))

}


// example

ulist := new(UserList)

if err := queryRows(queryString, ulist, arg1, arg2); err != nil {

    panic(err)

}


// or

u := new(User)

if err := queryRow(queryString, u, arg1, arg2); err != nil {

    panic(err)

}

如果您有想要掃描的復合類型,但又想避免重復枚舉其元素的字段,那么您可以引入一種返回類型字段的方法,并在需要的地方重用該方法。例如:


func (u *User) ScannableFields() []interface{} {

    return []interface{}{

        &u.Id,

        &u.Username,

        // ...

    }

}


func (u *User) ScanRow(r Row) error {

    return r.Scan(u.ScannableFields()...)

}


// your other entity type

type Site struct {

    Id   uint

    Name string

    // ...

}


func (s *Site) ScannableFields() []interface{} {

    return []interface{}{

        &p.Id,

        &p.Name,

        // ...

    }

}


// Implements RowScanner

func (s *Site) ScanRow(r Row) error {

    return r.Scan(s.ScannableFields()...)

}


// your composite

type UserWithSite struct {

    User *User

    Site *Site

}


// Implements RowScanner

func (u *UserWithSite) ScanRow(r Row) error {

    u.User = new(User)

    u.Site = new(Site)

    fields := append(u.User.ScannableFields(), u.Site.ScannableFields()...)

    return r.Scan(fields...)

}


// retrieve from db

u := new(UserWithSite)

if err := queryRow(queryString, u, arg1, arg2); err != nil {

    panic(err)

}



查看完整回答
反對 回復 2023-05-08
  • 1 回答
  • 0 關注
  • 108 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號