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 关注
- 189 浏览
添加回答
举报