У меня есть структура с картой объектов. Я хочу хранить элементы этой карты в отдельной таблице. Мне удалось заставить его работать для одной структуры FooEntry
, используя хуки AfterFind()
и BeforeSave()
; первый вызывается после загрузки массива ссылок. Он отлично работает при загрузке определенной строки FooEntryDb
.
Однако тот же самый код не работает для структуры FooParent
, имеющей массив FooEntryDb
. Проблема в том, что при считывании строки FooParent
ловушка AfterFind()
вызывается перед добавлением ссылок FooEntryDb
.
Мой запрос и код следующие
Запрос
var pdb api.FooParentDb
result := db.
Joins("FooEntriesDb").
Preload("FooEntriesDb.FooEntryLinks").
Find(&pdb, 104185244543150166)
Код
type FooParent struct {
FooParentPK uint64 `gorm:"primary_key;autoIncrement:false;column:foo_parent_pk;type:INT8;"`
Entries []*FooEntry `gorm:"-"`
}
type FooParentDb struct {
FooParent
FooEntriesDb []*FooEntryDb `gorm:"foreignKey:FooParentPK;constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"`
}
// TableName overrides the table name used by FooParentDb to `foo_parents`
func (FooParentDb) TableName() string {
return "foo_parents"
}
func (tdb *FooParentDb) AfterFind(tx *gorm.DB) (err error) {
if tx.Error == nil {
for _, edb := range tdb.FooEntriesDb {
edb.FooParentPK = tdb.FooParentPK
tdb.Entries = append(tdb.Entries, &edb.FooEntry)
}
}
return
}
func (tdb *FooParentDb) BeforeSave(tx *gorm.DB) (err error) {
for _, e := range tdb.Entries {
tdb.FooEntriesDb = append(tdb.FooEntriesDb, &FooEntryDb{FooEntry: *e, FooParentPK: tdb.FooParentPK})
}
return
}
// FooEntry holds one row from the foo_entries table
type FooEntry struct {
FooEntryPK uint64 `gorm:"primary_key;autoIncrement:false;column:foo_entry_pk;type:INT8;"`
Links map[string]int64 `gorm:"-"`
}
// FooEntryDb holds one row from the foo_entries table
type FooEntryDb struct {
FooEntry
FooParentPK uint64 `gorm:"association_foreignkey:FooParentPK;column:foo_parent_pk;type:INT8;"`
FooLinks []FooLink `gorm:"foreignKey:EntryPK;constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"`
}
// TableName overrides the table name used by FooEntryDb to `foo_entries`
func (FooEntryDb) TableName() string {
return "foo_entries"
}
func (edb *FooEntryDb) AfterFind(tx *gorm.DB) (err error) {
if tx.Error == nil {
edb.Links = make(map[string]int64)
for _, l := range edb.FooLinks {
if l.Key == "" {
return fmt.Errorf("empty key for entry links")
}
edb.Links[l.Key] = l.Link
}
}
return
}
func (edb *FooEntryDb) BeforeSave(tx *gorm.DB) (err error) {
var sorted []string
for key := range edb.Links {
sorted = append(sorted, key)
}
sort.Slice(sorted, func(i, j int) bool { return sorted[i] < sorted[j] })
edb.FooLinks = nil
for _, key := range sorted {
edb.FooLinks = append(edb.FooLinks, FooLink{Key: key, Link: edb.Links[key]})
}
return
}
// FooLink holds one row from the foo_entry_links table
type FooLink struct {
FooEntryPK uint64 `gorm:"primary_key;autoIncrement:false;association_foreignkey:FooEntryPK;column:foo_entry_pk;type:INT8;"`
Key string `gorm:"primary_key;column:foo_key;type:VARCHAR;size:64;"`
Link int64 `gorm:"column:foo_link;type:INT4;"`
}
// TableName overrides the table name used by FooLink to `foo_entry_links`
func (FooLink) TableName() string {
return "foo_entry_links"
}
FooEntryDb
, егоAfterFind
вызывается после того, как всеFooLinks
были загружены? Можете ли вы опубликовать код, который вы используете для загрузки этого примера? Что я могу представить, так это то, что может быть несоответствие в порядке вызоваAfterFind
между загрузкой отношения с использованиемJoins
и использованиемPreload
. - person Ezequiel Muns   schedule 13.01.2021