Сканирование в структуру, которая имеет карту структур

У меня есть структура с картой объектов. Я хочу хранить элементы этой карты в отдельной таблице. Мне удалось заставить его работать для одной структуры 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"
}

person Alan Cabrera    schedule 12.01.2021    source источник
comment
Можете ли вы подробно описать, какие результаты не соответствуют вашим ожиданиям, есть ли у вас ошибки?   -  person Ezequiel Muns    schedule 13.01.2021
comment
Во-вторых, когда вы загружаете один FooEntryDb, его AfterFind вызывается после того, как все FooLinks были загружены? Можете ли вы опубликовать код, который вы используете для загрузки этого примера? Что я могу представить, так это то, что может быть несоответствие в порядке вызова AfterFind между загрузкой отношения с использованием Joins и использованием Preload.   -  person Ezequiel Muns    schedule 13.01.2021
comment
@EzequielMuns, вот рабочий пример: github.com/maguro/bugs/tree/ main / gorm / hooks. Я ожидаю, что тесты пройдут.   -  person Alan Cabrera    schedule 15.01.2021