我是 golang 的新手,所以我從 udemy 購買了一些課程來幫助我學習這門語言。當我使用該語言進行一個專案時,我發現其中一個對一般理解非常有幫助。
在我參加的課程中,所有與 sql 相關的函式都在 sqlc 檔案夾中,結構較少:
sqlc
generatedcode
store
其中一個檔案是由 sqlc 生成的查詢器,該查詢器包含與所有生成的方法的介面。這是它目前看起來的一般想法:https ://github.com/techschool/simplebank/tree/master/db/sqlc
package db
import (
"context"
"github.com/google/uuid"
)
type Querier interface {
AddAccountBalance(ctx context.Context, arg AddAccountBalanceParams) (Account, error)
CreateAccount(ctx context.Context, arg CreateAccountParams) (Account, error)
...
}
var _ Querier = (*Queries)(nil)
是否可以將 sqlc 生成的內容和開發人員創建的任何查詢(動態查詢)包裝到單個查詢器中?我也試圖擁有它,以便 sqlc 生成的代碼位于它自己的檔案夾中。我的目標是:
sql
sqlc
generatedcode
store - (wraps it all together)
dynamicsqlfiles
這應該清楚我所說的商店的意思:https ://github.com/techschool/simplebank/blob/master/db/sqlc/store.go
package db
import (
"context"
"database/sql"
"fmt"
)
// Store defines all functions to execute db queries and transactions
type Store interface {
Querier
TransferTx(ctx context.Context, arg TransferTxParams) (TransferTxResult, error)
}
// SQLStore provides all functions to execute SQL queries and transactions
type SQLStore struct {
db *sql.DB
*Queries
}
// NewStore creates a new store
func NewStore(db *sql.DB) Store {
return &SQLStore{
db: db,
Queries: New(db),
}
}
我正在嘗試通過該存盤運行所有內容(生成的函式和我的函式),因此我可以在此檔案(server.store.)中進行類似于 CreateUser 函式的呼叫:https ://github.com/techschool/simplebank /blob/master/api/user.go
arg := db.CreateUserParams{
Username: req.Username,
HashedPassword: hashedPassword,
FullName: req.FullName,
Email: req.Email,
}
user, err := server.store.CreateUser(ctx, arg)
if err != nil {
if pqErr, ok := err.(*pq.Error); ok {
switch pqErr.Code.Name() {
case "unique_violation":
ctx.JSON(http.StatusForbidden, errorResponse(err))
return
}
}
ctx.JSON(http.StatusInternalServerError, errorResponse(err))
return
}
我嘗試創建一個包含另一個查詢器介面的東西,該介面嵌入了生成的查詢器介面,然后創建了我自己的 db.go,它使用生成的 DBTX 介面,但有自己的 Queries 結構和 New 函式。它總是給我一個錯誤,即我創建的 Queries 結構沒有實作我制作的功能,盡管它是在我制作的自定義方法之一中實作的。
我洗掉了那個分支,并一直在點擊上面鏈接的 simplebank 專案,看看我是否能找到另一種方法來完成,或者我是否錯過了一些東西。如果做不到,也沒關系。我只是利用這個機會來學習更多關于該語言的知識,并盡可能將一些代碼分開。
更新:我只需要更改幾件,但我修改了 store.go 看起來更像:
// sdb is imported, but points to the generated Querier
// Store provides all functions to execute db queries and transactions
type Store interface {
sdb.Querier
DynamicQuerier
}
// SQLStore provides all functions to execute SQL queries and transactions
type SQLStore struct {
db *sql.DB
*sdb.Queries
*dynamicQueries
}
// NewStore creates a new Store
func NewStore(db *sql.DB) Store {
return &SQLStore{
db: db,
Queries: sdb.New(db),
dynamicQueries: New(db),
}
}
然后為我將要創建的方法創建了一個新的查詢器和結構。給他們自己的新功能,并在上面捆綁在一起。之前,我試圖找出一種方法來盡可能多地重用生成的代碼,我認為這是問題所在。
為什么我想要界面:我想要一個結構,將我將要處理的檔案與我永遠不會接觸(生成)的檔案分開。這是新的結構:
我喜歡生成的代碼如何將所有內容放在查詢器介面中,然后檢查實作它的任何內容是否滿足所有功能要求。所以我想為我自己創建的動態部分復制它。
它可能比它“需要”更復雜一點,但它還提供了一組額外的錯誤檢查,這是很好的。在這種情況下,即使可能沒有必要,它最終還是可行的。
uj5u.com熱心網友回復:
是否可以將 sqlc 生成的內容和開發人員創建的任何查詢(動態查詢)包裝到單個查詢器中?
如果我正確理解了您的問題,我認為您正在尋找類似以下的內容(操場):
package main
import (
"context"
"database/sql"
)
// Sample SQL C Code
type DBTX interface {
ExecContext(context.Context, string, ...interface{}) (sql.Result, error)
PrepareContext(context.Context, string) (*sql.Stmt, error)
QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)
QueryRowContext(context.Context, string, ...interface{}) *sql.Row
}
type Queries struct {
db DBTX
}
func (q *Queries) DeleteAccount(ctx context.Context, id int64) error {
// _, err := q.db.ExecContext(ctx, deleteAccount, id)
// return err
return nil // Pretend that this always works
}
type Querier interface {
DeleteAccount(ctx context.Context, id int64) error
}
//
// Your custom "dynamic" queries
//
type myDynamicQueries struct {
db DBTX
}
func (m *myDynamicQueries) GetDynamicResult(ctx context.Context) error {
// _, err := q.db.ExecContext(ctx, deleteAccount, id)
// return err
return nil // Pretend that this always works
}
type myDynamicQuerier interface {
GetDynamicResult(ctx context.Context) error
}
// Combine things
type allDatabase struct {
*Queries // Note: You could embed this directly into myDynamicQueries instead of having a seperate struct if that is your preference
*myDynamicQueries
}
type DatabaseFunctions interface {
Querier
myDynamicQuerier
}
func main() {
// Basic example
var db DatabaseFunctions
db = getDatabase()
db.DeleteAccount(context.Background(), 0)
db.GetDynamicResult(context.Background())
}
// getDatabase - Perform whatever is needed to connect to database...
func getDatabase() allDatabase {
sqlc := &Queries{db: nil} // In reality you would use New() to do this!
myDyn := &myDynamicQueries{db: nil} // Again it's often cleaner to use a function
return allDatabase{Queries: sqlc, myDynamicQueries: myDyn}
}
為簡單起見,以上所有內容都在一個檔案中,但可以輕松地從多個包中提取,例如
type allDatabase struct {
*generatedcode.Queries
*store.myDynamicQueries
}
如果這不能回答您的問題,那么請展示您失敗的嘗試之一(這樣我們就可以看到您哪里出錯了)。
一個一般性評論——你真的需要這個界面嗎?一個常見的建議是“接受介面,回傳結構”。雖然這可能并不總是適用,但我懷疑您可能會在并非真正需要的地方引入介面,這可能會增加不必要的復雜性。
我認為容納兩個查詢者的商店正在將它們捆綁在一起。你能用上面的例子(在問題帖子中)解釋一下為什么沒有必要嗎?SQLStore 如何訪問所有 Querier 介面函式?
該結構SQLStore是“將它們捆綁在一起”。根據Go 規范:
給定一個結構型別 S 和一個命名型別 T,提升的方法包含在結構的方法集中,如下所示:
- 如果 S 包含嵌入欄位 T,則 S 和 *S 的方法集都包含帶有接收者 T 的提升方法。*S 的方法集還包括帶有接收者 *T 的提升方法。
- 如果 S 包含嵌入欄位 *T,則 S 和 *S 的方法集都包含帶有接收器 T 或 *T 的提升方法。
所以一個型別的物件SQLStore:
type SQLStore struct {
db *sql.DB
*sdb.Queries
*dynamicQueries
}
var foo SQLStore // Assume that we are actually providing values for all fields
將實作中的所有方法,sdb.Queries以及dynamicQueries(您也可以sql.DB通過 訪問成員foo.db.XXX)中的方法。這意味著您可以呼叫foo.AddAccountBalance()and foo.MyGenericQuery()(假設在dynamicQueries!)等。
該規范說“在其最基本的形式中,介面指定了一個(可能為空的)方法串列”。因此,您可以將介面視為必須由您分配給介面的任何實作(例如struct)實作的功能串列(介面本身不直接實作任何東西)。
這個例子可以幫助你理解。
希望這會有所幫助(因為我不確定您不了解哪個方面,我不確定要關注什么)。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/505080.html
