3 回答

TA貢獻1995條經驗 獲得超2個贊
你的ByUnique已經幾乎是通用的了。只需拉出變化的部分(表格和目的地):
func ByUnique(table string, column string, value interface{}, dest interface{}) error {
query := fmt.Sprintf(`
SELECT *
FROM %s
WHERE %s = ?
LIMIT 1;
`, table, column)
return sql.DB.QueryRowx(query, value).StructScan(dest)
}
func ByUniqueMessage(column string, value interface{}) (*Message, error) {
message := &Message{}
if err := ByUnique("message", column, value, &message); err != nil {
return nil, err
}
return message, error
}
你save的非常相似。您只需要按照以下方式創建一個通用的保存功能:
func Save(table string, identifier int64, source interface{}) { ... }
然后在 內部(*Message)save,您只需調用通用Save()函數即可??雌饋砗芎唵?。
旁注:不要this在方法內用作對象的名稱。有關更多信息,請參閱@OneOfOne 的鏈接。不要沉迷于 DRY。它本身并不是一個目標。Go 專注于代碼簡單、清晰和可靠。不要為了避免輸入簡單的錯誤處理行而創建復雜和脆弱的東西。這并不意味著您不應該提取重復的代碼。這只是意味著在 Go 中,通常最好重復一點簡單的代碼,而不是創建復雜的代碼來避免它。
編輯:如果你想Save使用接口來實現,那沒問題。只需創建一個Identifier接口。
type Ider interface {
Id() int64
SetId(newId int64)
}
func (msg *Message) Id() int64 {
return msg.Id
}
func (msg *Message) SetId(newId int64) {
msg.Id = newId
}
func Save(table string, source Ider) error {
s := ""
if source.Id() == 0 {
s = fmt.Sprintf("INSERT INTO %s SET %%s;", table)
} else {
s = fmt.Sprintf("UPDATE %s SET %%s WHERE id=:id;", table)
}
query := fmt.Sprintf(s, sql.PlaceholderPairs(source))
nstmt, err := sql.DB.PrepareNamed(query)
if err != nil {
return err
}
res, err := nstmt.Exec(source)
if err != nil {
return err
}
if source.Id() == 0 {
lastId, err := res.LastInsertId()
if err != nil {
return err
}
source.SetId(lastId)
}
return nil
}
func (msg *Message) save() error {
return Save("message", msg)
}
可能會因此而爆炸的一個部分是調用Exec. 我不知道你使用的是什么包,Exec如果你傳遞一個接口而不是實際的結構,它可能無法正常工作,但它可能會工作。也就是說,我可能只是傳遞標識符而不是添加這個開銷。

TA貢獻1859條經驗 獲得超6個贊
您可能想要使用 ORM。它們消除了您描述的許多樣板代碼。
見這個問題對于“什么是ORM?”
以下是 go 的 ORM 列表:https : //github.com/avelino/awesome-go#orm
我自己從來沒有用過一個,所以我不能推薦任何一個。主要原因是 ORM 需要開發人員的大量控制并引入不可忽略的性能開銷。您需要親眼看看它們是否適合您的用例和/或您是否對這些庫中正在發生的“魔法”感到滿意。

TA貢獻1876條經驗 獲得超6個贊
我不建議這樣做,我個人更喜歡明確地掃描結構和創建查詢。
但如果你真的想堅持反思,你可以這樣做:
func ByUnique(obj interface{}, column string, value interface{}) error {
// ...
return sql.DB.QueryRowx(query, value).StructScan(obj)
}
// Call with
message := &Message{}
ByUnique(message, ...)
為了您的保存:
type Identifiable interface {
Id() int64
}
// Implement Identifiable for message, etc.
func Save(obj Identifiable) error {
// ...
}
// Call with
Save(message)
我使用并推薦給您的方法:
type Redirect struct {
ID string
URL string
CreatedAt time.Time
}
func FindByID(db *sql.DB, id string) (*Redirect, error) {
var redirect Redirect
err := db.QueryRow(
`SELECT "id", "url", "created_at" FROM "redirect" WHERE "id" = $1`, id).
Scan(&redirect.ID, &redirect.URL, &redirect.CreatedAt)
switch {
case err == sql.ErrNoRows:
return nil, nil
case err != nil:
return nil, err
}
return &redirect, nil
}
func Save(db *sql.DB, redirect *Redirect) error {
redirect.CreatedAt = time.Now()
_, err := db.Exec(
`INSERT INTO "redirect" ("id", "url", "created_at") VALUES ($1, $2, $3)`,
redirect.ID, redirect.URL, redirect.CreatedAt)
return err
}
這具有使用類型系統并僅映射它應該實際映射的內容的優點。
- 3 回答
- 0 關注
- 234 瀏覽
添加回答
舉報