1 回答

TA貢獻(xiàn)1796條經(jīng)驗(yàn) 獲得超10個(gè)贊
在評(píng)論中反復(fù)討論之后,我將在此處發(fā)布這個(gè)最小的答案。這絕不是一個(gè)明確的“這就是你所做的”類型的答案,但我希望這至少可以為你提供足夠的信息來幫助你開始。為了達(dá)到這一點(diǎn),我根據(jù)您提供的代碼片段做出了一些假設(shè),并且我假設(shè)您希望通過某種命令(例如)為數(shù)據(jù)庫播種your_bin seed
。這意味著做出了以下假設(shè):
存在模式和相應(yīng)的模型/類型(類似
AirportCodes
等)每種類型都有自己的源文件(名稱來自
Seed()
方法,返回一個(gè).json
文件名)因此,假定種子數(shù)據(jù)的格式類似于
[{"seed": "data"}, {"more": "data"}]
.可以附加種子文件,如果模式發(fā)生變化,種子文件中的數(shù)據(jù)也可以一起更改。這在 ATM 中不太重要,但仍然是一個(gè)應(yīng)該注意的假設(shè)。
好的,讓我們首先將所有 JSON 文件移動(dòng)到可預(yù)測(cè)的位置。在一個(gè)相當(dāng)大的真實(shí)世界應(yīng)用程序中,您會(huì)使用類似XDG 基本路徑的東西,但為了簡(jiǎn)潔起見,我們假設(shè)您在一個(gè)臨時(shí)容器中運(yùn)行它,并且/
所有相關(guān)資產(chǎn)都已復(fù)制到所述容器中。
將所有種子文件放在seed_data
目錄下的基本路徑中是有意義的。每個(gè)文件都包含特定表的種子數(shù)據(jù),因此一個(gè)文件中的所有數(shù)據(jù)都整齊地映射到一個(gè)模型上。讓我們暫時(shí)忽略關(guān)系數(shù)據(jù)。我們只是假設(shè),現(xiàn)在,這些文件中的數(shù)據(jù)至少在內(nèi)部是一致的,并且任何X-to-X
關(guān)系數(shù)據(jù)都必須具有正確的 ID 字段,允許 JOIN 等。
開始吧
所以我們有了我們的模型,以及 JSON 文件中的數(shù)據(jù)。現(xiàn)在我們可以只創(chuàng)建所述模型的一部分,確保在插入其他數(shù)據(jù)之前您想要/需要存在的數(shù)據(jù)表示為比另一個(gè)更高的條目(較低的索引)。有點(diǎn)像這樣:
seederModelList = []globals.Seeder{
m.AirportCodes{}, // seeds before Term
m.Term{}, // seeds after AirportCodes
}
但是,或者從這個(gè)方法返回文件名Seed,為什么不傳入連接并讓模型像這樣處理自己的數(shù)據(jù):
func (_ AirportCodes) Seed(db *gorm.DB) error {
// we know what file this model uses
data, err := os.ReadFile("seed_data/airport_codes.json")
if err != nil {
return err
}
// we have the data, we can unmarshal it as AirportCode instances
codes := []*AirportCodes{}
if err := json.Unmarshal(data, &codes); err != nil {
return err
}
// now INSERT, UPDATE, or UPSERT:
db.Clauses(clause.OnConflict{
UpdateAll: true,
}).Create(&codes)
}
對(duì)其他模型執(zhí)行相同的操作,例如Terms:
func (_ Terms) Seed(db *gorm.DB) error {
// we know what file this model uses
data, err := os.ReadFile("seed_data/terms.json")
if err != nil {
return err
}
// we have the data, we can unmarshal it as Terms instances
terms := []*Terms{}
if err := json.Unmarshal(data, &terms); err != nil {
return err
}
// now INSERT, UPDATE, or UPSERT:
return db.Clauses(clause.OnConflict{
UpdateAll: true,
}).Create(&terms)
}
當(dāng)然,考慮到我們?cè)谀P椭杏袛?shù)據(jù)庫訪問權(quán)限,這確實(shí)會(huì)導(dǎo)致一些混亂,如果你問我的話,它實(shí)際上應(yīng)該只是一個(gè) DTO。這在錯(cuò)誤處理方面也有很多不足之處,但它的基本要點(diǎn)是:
func main() {
db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{}) // omitted error handling for brevity
seeds := []interface{
Seed(*gorm.DB) error
}{
model.AirportCodes{},
model.Terms{},
// etc...
}
for _, m := range seeds {
if err := m.Seed(db); err != nil {
panic(err)
}
}
db.Close()
}
好的,這應(yīng)該讓我們開始了,但讓我們通過以下方式將這一切變成更好的東西:
將整個(gè)數(shù)據(jù)庫交互移出 DTO/模型
將事情包裝到事務(wù)中,這樣我們就可以回滾錯(cuò)誤
稍微更新初始切片以使事情更清晰
因此,如前所述,我假設(shè)您有存儲(chǔ)庫之類的東西來處理單獨(dú)包中的數(shù)據(jù)庫交互。我們不應(yīng)該調(diào)用Seed
模型并將數(shù)據(jù)庫連接傳遞給模型,而應(yīng)該依賴我們的存儲(chǔ)庫:
db, _ := gorm.Open() // same as before
acs := repo.NewAirportCodes(db) // pass in connection
tms := repo.NewTerms(db) // again...
現(xiàn)在我們的模型仍然可以返回 JSON 文件名,或者我們可以將其作為constrepos 中的一個(gè)。在這一點(diǎn)上,這并不重要。最主要的是,我們可以在存儲(chǔ)庫中完成實(shí)際的數(shù)據(jù)插入。
如果你愿意,你可以將你的種子切片更改為這樣的東西:
calls := []func() error{
acs.Seed, // assuming your repo has a Seed function that does what it's supposed to do
tms.Seed,
}
然后循環(huán)執(zhí)行所有播種:
for _, c := range calls {
if err := c(); err != nil {
panic(err)
}
}
現(xiàn)在,這只剩下交易問題了。值得慶幸的是,gorm 使這變得非常簡(jiǎn)單:
db, _ := gorm.Open()
db.Transaction(func(tx *gorm.DB) error {
acs := repo.NewAirportCodes(tx) // create repo's, but use TX for connection
if err := acs.Seed(); err != nil {
return err // returning an error will automatically rollback the transaction
}
tms := repo.NewTerms(tx)
if err := tms.Seed(); err != nil {
return err
}
return nil // commit transaction
})
您可以在這里擺弄更多的東西,例如創(chuàng)建可以單獨(dú)提交的相關(guān)數(shù)據(jù)批次,您可以添加更精確的錯(cuò)誤處理和更多信息的日志記錄,更好地處理沖突(區(qū)分 CREATE 和 UPDATE 等...)。不過,最重要的是,有一點(diǎn)值得牢記:
Gorm有一個(gè)遷移系統(tǒng)
我不得不承認(rèn),我已經(jīng)有一段時(shí)間沒有處理 gorm 了,但是 IIRC,如果模型發(fā)生變化,你可以讓表自動(dòng)遷移,并在啟動(dòng)時(shí)運(yùn)行自定義 go 代碼和/或 SQL 文件,這些都可以使用,相當(dāng)容易地播種數(shù)據(jù)??赡苤档醚芯恳幌滤目尚行浴?/p>
- 1 回答
- 0 關(guān)注
- 97 瀏覽
添加回答
舉報(bào)