7

我有以下人为的例子:

type Top struct {
  ID     uint `gorm:"primary_key"`
  Name   string
  Middle []*Middle
}

type Middle struct {
  ID    uint `gorm:"primary_key"`
  TopID int
  Name  string
  Low   []*Low
}

type Low struct {
  ID       uint `gorm:"primary_key"`
  MiddleID int
  Name     string
  Bottom   []*Bottom
}

type Bottom struct {
  ID    uint `gorm:"primary_key"`
  LowID int
  Name  string
}
top := Top{
  Name: "Top",
  Middle: []*Middle{
    {
      Name: "Middle",
      Low: []*Low{
        {
          Name: "Low",
          Bottom: []*Bottom{
            {
              Name: "Bottom",
            },
          },
        },
      },
    },
  },
}

if err := db.Save(&top).Error; err != nil {
  log.Fatal("Got errors when saving calc", err.Error())
}

if err := db.Where("id = 1").
  Preload("Middle.Low.Bottom").
  First(&top).Error; err != nil {
  log.Fatal(err)
}

这给了我以下输出:

(/host_home/workspace/golang/src/bitbucket.org/cloudgloballog/gormtest/main.go:81)
[2016-03-18 01:53:23]  [4.37ms]  INSERT INTO "tops" ("name") VALUES ('Top') RETURNING "tops"."id"

(/host_home/workspace/golang/src/bitbucket.org/cloudgloballog/gormtest/main.go:81)
[2016-03-18 01:53:23]  [3.49ms]  INSERT INTO "middles" ("name","top_id") VALUES ('Middle','1') RETURNING "middles"."id"

(/host_home/workspace/golang/src/bitbucket.org/cloudgloballog/gormtest/main.go:81)
[2016-03-18 01:53:23]  [1.07ms]  INSERT INTO "lows" ("middle_id","name") VALUES ('1','Low') RETURNING "lows"."id"

()
[2016-03-18 01:53:23]  [9.16ms]  INSERT INTO "bottoms" ("low_id","name") VALUES ('1','Bottom') RETURNING "bottoms"."id"

(/host_home/workspace/golang/src/bitbucket.org/cloudgloballog/gormtest/main.go:86)
[2016-03-18 01:53:23]  [1.54ms]  SELECT  * FROM "tops"   ORDER BY "tops"."id" ASC LIMIT 1

(/host_home/workspace/golang/src/bitbucket.org/cloudgloballog/gormtest/main.go:88)
[2016-03-18 01:53:23]  [2.63ms]  SELECT  * FROM "tops"  WHERE ("id" = '1') ORDER BY "tops"."id" ASC LIMIT 1

(/host_home/workspace/golang/src/bitbucket.org/cloudgloballog/gormtest/main.go:88)
[2016-03-18 01:53:23]  [1.65ms]  SELECT  * FROM "middles"  WHERE (top_id IN ('1')) ORDER BY "middles"."id" ASC

(/host_home/workspace/golang/src/bitbucket.org/cloudgloballog/gormtest/main.go:88)
[2016-03-18 01:53:23]  [4.20ms]  SELECT  * FROM "lows"  WHERE (middle_id IN ('1')) ORDER BY "lows"."id" ASC

(/host_home/workspace/golang/src/bitbucket.org/cloudgloballog/gormtest/main.go:88)
[2016-03-18 01:53:23]  can't find field Bottom in []**main.Low

如果我只嵌套 Top -> Middle -> Low 并调用Preload("Middle.Low")它可以正常工作。线索可能在双指针引用中[]**main.Low,但我无法弄清楚如何正确执行此操作。

此级别可能不支持嵌套预加载。有人知道吗?

4

1 回答 1

2

此问题似乎已出现在早期版本的 gorm 中,如:Issue1Issue2Issue3中所述,但最新的预加载问题修复已在此版本中

看起来它已经解决了很长一段时间,但是除非有人搜索 git 提交日期,否则它所修复的确切版本没有记录。

使用最新版本(撰写本文时为 v1.9.11)设置当前示例没有问题。

package main

import (
    "log"

    "github.com/davecgh/go-spew/spew"
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/sqlite"
)

type Top struct {
    ID     uint `gorm:"primary_key"`
    Name   string
    Middle []*Middle
}

type Middle struct {
    ID    uint `gorm:"primary_key"`
    TopID int
    Name  string
    Low   []*Low
}

type Low struct {
    ID       uint `gorm:"primary_key"`
    MiddleID int
    Name     string
    Bottom   []*Bottom
}

type Bottom struct {
    ID    uint `gorm:"primary_key"`
    LowID int
    Name  string
}

func main() {
    db, err := gorm.Open("sqlite3", "test.db")
    if err != nil {
        panic("failed to connect database")
    }
    defer db.Close()
    // Enable Logger, show detailed log
    db.LogMode(true)
    // Migrate the schema
    db.AutoMigrate(&Top{})
    db.AutoMigrate(&Middle{})
    db.AutoMigrate(&Low{})
    db.AutoMigrate(&Bottom{})

    top := Top{
        Name: "Top",
        Middle: []*Middle{
            {
                Name: "Middle",
                Low: []*Low{
                    {
                        Name: "Low",
                        Bottom: []*Bottom{
                            {
                                Name: "Bottom",
                            },
                        },
                    },
                },
            },
        },
    }

    if err := db.Save(&top).Error; err != nil {
        log.Fatal("Got errors when saving calc", err.Error())
    }
    var ntop Top
    if err := db.Where("id = 1").
        Preload("Middle.Low.Bottom").
        First(&ntop).Error; err != nil {
        log.Fatal(err)
    }

    spew.Dump(&ntop)
}

$ ./main.exe

(C:/Users/fakename/source/stackoverflow/go/gorm-nested/main.go:70)
[2019-10-31 14:44:23]  [2.00ms]  INSERT  INTO "tops" ("name") VALUES ('Top')
[1 rows affected or returned ]

(C:/Users/fakename/source/stackoverflow/go/gorm-nested/main.go:70)
[2019-10-31 14:44:23]  [0.00ms]  INSERT  INTO "middles" ("top_id","name") VALUES (1,'Middle')
[1 rows affected or returned ]

(C:/Users/fakename/source/stackoverflow/go/gorm-nested/main.go:70)
[2019-10-31 14:44:23]  [0.99ms]  INSERT  INTO "lows" ("middle_id","name") VALUES (1,'Low')
[1 rows affected or returned ]

()
[2019-10-31 14:44:23]  [0.00ms]  INSERT  INTO "bottoms" ("low_id","name") VALUES (1,'Bottom')
[1 rows affected or returned ]

(C:/Users/fakename/source/stackoverflow/go/gorm-nested/main.go:76)
[2019-10-31 14:44:23]  [0.00ms]  SELECT * FROM "tops"  WHERE (id = 1) ORDER BY "tops"."id" ASC LIMIT 1
[1 rows affected or returned ]

(C:/Users/fakename/source/stackoverflow/go/gorm-nested/main.go:76)
[2019-10-31 14:44:23]  [0.00ms]  SELECT * FROM "middles"  WHERE ("top_id" IN (1)) ORDER BY "middles"."id" ASC
[1 rows affected or returned ]

(C:/Users/fakename/source/stackoverflow/go/gorm-nested/main.go:76)
[2019-10-31 14:44:23]  [0.00ms]  SELECT * FROM "lows"  WHERE ("middle_id" IN (1)) ORDER BY "lows"."id" ASC
[1 rows affected or returned ]

(C:/Users/fakename/source/stackoverflow/go/gorm-nested/main.go:76)
[2019-10-31 14:44:23]  [0.99ms]  SELECT * FROM "bottoms"  WHERE ("low_id" IN (1)) ORDER BY "bottoms"."id" ASC
[1 rows affected or returned ]
(*main.Top)(0xc00015f950)({
 ID: (uint) 1,
 Name: (string) (len=3) "Top",
 Middle: ([]*main.Middle) (len=1 cap=1) {
  (*main.Middle)(0xc000150fc0)({
   ID: (uint) 1,
   TopID: (int) 1,
   Name: (string) (len=6) "Middle",
   Low: ([]*main.Low) (len=1 cap=1) {
    (*main.Low)(0xc000151080)({
     ID: (uint) 1,
     MiddleID: (int) 1,
     Name: (string) (len=3) "Low",
     Bottom: ([]*main.Bottom) (len=1 cap=1) {
      (*main.Bottom)(0xc0001929c0)({
       ID: (uint) 1,
       LowID: (int) 1,
       Name: (string) (len=6) "Bottom"
      })
     }
    })
   }
  })
 }
})

于 2019-10-31T21:46:20.077 回答