[英]What's the best way to handle database errors in Go without getting coupled to the SQL driver?
在 Go 中與 SQL 數據庫交互的常用方法是使用內置的database/sql
接口。 許多不同的第三方包以特定於某些特定數據庫的方式實現此接口,而不會將其工作暴露給消費者,例如 Postgres 驅動程序、MySQL 驅動程序等。
但是, database/sql
不提供任何特定的錯誤類型,而是由驅動程序決定。 這帶來了一個問題:除了 nil 檢查之外,您對這些特定錯誤所做的任何錯誤處理現在都基於特定驅動程序的假設。 如果您決定稍后更改驅動程序,則必須修改所有錯誤處理代碼。 如果您想支持多個驅動程序,您還需要為該驅動程序編寫額外的檢查。
這似乎破壞了使用接口的主要好處:具有商定合同的可移植性。
這是一個使用jackc/pgx/v4/stdlib
驅動程序和幫助程序包套件來說明此問題的示例:
import (
"database/sql"
"errors"
"github.com/jackc/pgconn"
"github.com/jackc/pgerrcode"
)
// Omitted code for the sake of simplification, err comes from database/sql
if err != nil {
var pgerr *pgconn.PgError
if errors.As(err, &pgerr) {
if pgerrcode.IsIntegrityConstraintViolation(pgerr.SQLState()) {
return nil, errors.New("related entity does not exist")
}
}
// If we wanted to support another database driver, we'd have to include that here
return nil, errors.New("failed to insert the thing")
}
如果我已經必須將特定於驅動程序的代碼放入我的 package 中,為什么還要接受database/sql
接口呢? 我可以改為需要特定的驅動程序,這可以說更安全,因為它可以防止消費者嘗試使用我們沒有錯誤處理的其他不受支持的驅動程序。
是否有更好的方法來處理特定的database/sql
錯誤?
Package sql 圍繞 SQL(或類似 SQL)數據庫提供通用接口。
在提供最少的通用功能集和提供不適用於所有實現的功能之間存在折衷。 sql package 優先考慮前者,而您可能更喜歡后者。
您可能會爭辯說,每個可能的實現都應該能夠為您的示例提供特定的錯誤。 也許就是這樣。 也許不吧。 我不知道。
無論哪種方式,您都可以將pgerrcode.IsIntegrityConstraintViolation
包裝在 function 中,它會檢查您支持的每個驅動程序。 然后由您決定如何處理缺乏支持的驅動程序。
您不需要特定於驅動程序的代碼來獲取 SQLState。 例子:
func getSQLState(err error) {
type checker interface {
SQLState() string
}
pe := err.(checker)
log.Println("SQLState:", pe.SQLState())
}
但無論如何,SQLState 都是特定於數據庫的。 如果您將來切換到另一個數據庫/驅動程序,那么您需要手動更改所有錯誤代碼。 編譯器無助於檢測它。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.