在 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")
}
如果我已經必須將特定于驅動程式的代碼放入我的包中,為什么還要接受database/sql介面呢?我可以改為需要特定的驅動程式,這可以說更安全,因為它可以防止消費者嘗試使用我們沒有錯誤處理的其他不受支持的驅動程式。
有沒有更好的方法來處理特定的database/sql錯誤?
uj5u.com熱心網友回復:
包 sql 提供了一個圍繞 SQL(或類似 SQL)資料庫的通用介面。
在提供最少的通用功能集和提供不適用于所有實作的功能之間存在折衷。sql 包優先考慮前者,而您可能更喜歡后者。
您可能會爭辯說,每個可能的實作都應該能夠為您的示例提供特定的錯誤。也許就是這樣。也許不會。我不知道。
無論哪種方式,您都可以封裝pgerrcode.IsIntegrityConstraintViolation一個函式來檢查您支持的每個驅動程式。然后由您決定如何處理缺乏支持的驅動程式。
uj5u.com熱心網友回復:
您不需要特定于驅動程式的代碼來獲取 SQLState。例子:
func getSQLState(err error) {
type checker interface {
SQLState() string
}
pe := err.(checker)
log.Println("SQLState:", pe.SQLState())
}
但無論如何,SQLState 都是特定于資料庫的。如果您將來切換到另一個資料庫/驅動程式,那么您需要手動更改所有錯誤代碼。編譯器無助于檢測它。
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/422019.html
標籤:
