Golang gorm – 使用gorm预加载深层嵌套模型

huangapple go评论122阅读模式
英文:

Golang gorm - preload with deeply nested models

问题

我有一个人为的例子:

  1. type Top struct {
  2. ID uint `gorm:"primary_key"`
  3. Name string
  4. Middle []*Middle
  5. }
  6. type Middle struct {
  7. ID uint `gorm:"primary_key"`
  8. TopID int
  9. Name string
  10. Low []*Low
  11. }
  12. type Low struct {
  13. ID uint `gorm:"primary_key"`
  14. MiddleID int
  15. Name string
  16. Bottom []*Bottom
  17. }
  18. type Bottom struct {
  19. ID uint `gorm:"primary_key"`
  20. LowID int
  21. Name string
  22. }
  23. top := Top{
  24. Name: "Top",
  25. Middle: []*Middle{
  26. {
  27. Name: "Middle",
  28. Low: []*Low{
  29. {
  30. Name: "Low",
  31. Bottom: []*Bottom{
  32. {
  33. Name: "Bottom",
  34. },
  35. },
  36. },
  37. },
  38. },
  39. },
  40. }
  41. if err := db.Save(&top).Error; err != nil {
  42. log.Fatal("保存计算时出错", err.Error())
  43. }
  44. if err := db.Where("id = 1").
  45. Preload("Middle.Low.Bottom").
  46. First(&top).Error; err != nil {
  47. log.Fatal(err)
  48. }

这给出了以下输出:

  1. (/host_home/workspace/golang/src/bitbucket.org/cloudgloballog/gormtest/main.go:81)
  2. [2016-03-18 01:53:23] [4.37ms] INSERT INTO "tops" ("name") VALUES ('Top') RETURNING "tops"."id"
  3. (/host_home/workspace/golang/src/bitbucket.org/cloudgloballog/gormtest/main.go:81)
  4. [2016-03-18 01:53:23] [3.49ms] INSERT INTO "middles" ("name","top_id") VALUES ('Middle','1') RETURNING "middles"."id"
  5. (/host_home/workspace/golang/src/bitbucket.org/cloudgloballog/gormtest/main.go:81)
  6. [2016-03-18 01:53:23] [1.07ms] INSERT INTO "lows" ("middle_id","name") VALUES ('1','Low') RETURNING "lows"."id"
  7. ()
  8. [2016-03-18 01:53:23] [9.16ms] INSERT INTO "bottoms" ("low_id","name") VALUES ('1','Bottom') RETURNING "bottoms"."id"
  9. (/host_home/workspace/golang/src/bitbucket.org/cloudgloballog/gormtest/main.go:86)
  10. [2016-03-18 01:53:23] [1.54ms] SELECT * FROM "tops" ORDER BY "tops"."id" ASC LIMIT 1
  11. (/host_home/workspace/golang/src/bitbucket.org/cloudgloballog/gormtest/main.go:88)
  12. [2016-03-18 01:53:23] [2.63ms] SELECT * FROM "tops" WHERE ("id" = '1') ORDER BY "tops"."id" ASC LIMIT 1
  13. (/host_home/workspace/golang/src/bitbucket.org/cloudgloballog/gormtest/main.go:88)
  14. [2016-03-18 01:53:23] [1.65ms] SELECT * FROM "middles" WHERE (top_id IN ('1')) ORDER BY "middles"."id" ASC
  15. (/host_home/workspace/golang/src/bitbucket.org/cloudgloballog/gormtest/main.go:88)
  16. [2016-03-18 01:53:23] [4.20ms] SELECT * FROM "lows" WHERE (middle_id IN ('1')) ORDER BY "lows"."id" ASC
  17. (/host_home/workspace/golang/src/bitbucket.org/cloudgloballog/gormtest/main.go:88)
  18. [2016-03-18 01:53:23] 找不到 []**main.Low 中的 Bottom 字段

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

可能不支持到这个层次的嵌套预加载。有人知道吗?

英文:

I have the following contrived example:

  1. type Top struct {
  2. ID uint `gorm:"primary_key"`
  3. Name string
  4. Middle []*Middle
  5. }
  6. type Middle struct {
  7. ID uint `gorm:"primary_key"`
  8. TopID int
  9. Name string
  10. Low []*Low
  11. }
  12. type Low struct {
  13. ID uint `gorm:"primary_key"`
  14. MiddleID int
  15. Name string
  16. Bottom []*Bottom
  17. }
  18. type Bottom struct {
  19. ID uint `gorm:"primary_key"`
  20. LowID int
  21. Name string
  22. }
  23. top := Top{
  24. Name: "Top",
  25. Middle: []*Middle{
  26. {
  27. Name: "Middle",
  28. Low: []*Low{
  29. {
  30. Name: "Low",
  31. Bottom: []*Bottom{
  32. {
  33. Name: "Bottom",
  34. },
  35. },
  36. },
  37. },
  38. },
  39. },
  40. }
  41. if err := db.Save(&top).Error; err != nil {
  42. log.Fatal("Got errors when saving calc", err.Error())
  43. }
  44. if err := db.Where("id = 1").
  45. Preload("Middle.Low.Bottom").
  46. First(&top).Error; err != nil {
  47. log.Fatal(err)
  48. }

This gives me the following output:

  1. (/host_home/workspace/golang/src/bitbucket.org/cloudgloballog/gormtest/main.go:81)
  2. [2016-03-18 01:53:23] [4.37ms] INSERT INTO "tops" ("name") VALUES ('Top') RETURNING "tops"."id"
  3. (/host_home/workspace/golang/src/bitbucket.org/cloudgloballog/gormtest/main.go:81)
  4. [2016-03-18 01:53:23] [3.49ms] INSERT INTO "middles" ("name","top_id") VALUES ('Middle','1') RETURNING "middles"."id"
  5. (/host_home/workspace/golang/src/bitbucket.org/cloudgloballog/gormtest/main.go:81)
  6. [2016-03-18 01:53:23] [1.07ms] INSERT INTO "lows" ("middle_id","name") VALUES ('1','Low') RETURNING "lows"."id"
  7. ()
  8. [2016-03-18 01:53:23] [9.16ms] INSERT INTO "bottoms" ("low_id","name") VALUES ('1','Bottom') RETURNING "bottoms"."id"
  9. (/host_home/workspace/golang/src/bitbucket.org/cloudgloballog/gormtest/main.go:86)
  10. [2016-03-18 01:53:23] [1.54ms] SELECT * FROM "tops" ORDER BY "tops"."id" ASC LIMIT 1
  11. (/host_home/workspace/golang/src/bitbucket.org/cloudgloballog/gormtest/main.go:88)
  12. [2016-03-18 01:53:23] [2.63ms] SELECT * FROM "tops" WHERE ("id" = '1') ORDER BY "tops"."id" ASC LIMIT 1
  13. (/host_home/workspace/golang/src/bitbucket.org/cloudgloballog/gormtest/main.go:88)
  14. [2016-03-18 01:53:23] [1.65ms] SELECT * FROM "middles" WHERE (top_id IN ('1')) ORDER BY "middles"."id" ASC
  15. (/host_home/workspace/golang/src/bitbucket.org/cloudgloballog/gormtest/main.go:88)
  16. [2016-03-18 01:53:23] [4.20ms] SELECT * FROM "lows" WHERE (middle_id IN ('1')) ORDER BY "lows"."id" ASC
  17. (/host_home/workspace/golang/src/bitbucket.org/cloudgloballog/gormtest/main.go:88)
  18. [2016-03-18 01:53:23] can't find field Bottom in []**main.Low

If I were to only nest Top -> Middle -> Low and called Preload("Middle.Low") it works fine. The clue is probably in the double pointer reference []**main.Low but I'm unable to work out how to do this properly.

It's possible that nested preloading is not supported to this level. Anyone know?

答案1

得分: 2

这个问题似乎在早期版本的gorm中存在,如下所述:Issue1Issue2Issue3,但最新的预加载问题修复在这个链接中。

看起来这个问题已经解决了一段时间,但具体修复的版本没有记录,除非有人搜索git提交日期。

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

  1. package main
  2. import (
  3. "log"
  4. "github.com/davecgh/go-spew/spew"
  5. "github.com/jinzhu/gorm"
  6. _ "github.com/jinzhu/gorm/dialects/sqlite"
  7. )
  8. type Top struct {
  9. ID uint `gorm:"primary_key"`
  10. Name string
  11. Middle []*Middle
  12. }
  13. type Middle struct {
  14. ID uint `gorm:"primary_key"`
  15. TopID int
  16. Name string
  17. Low []*Low
  18. }
  19. type Low struct {
  20. ID uint `gorm:"primary_key"`
  21. MiddleID int
  22. Name string
  23. Bottom []*Bottom
  24. }
  25. type Bottom struct {
  26. ID uint `gorm:"primary_key"`
  27. LowID int
  28. Name string
  29. }
  30. func main() {
  31. db, err := gorm.Open("sqlite3", "test.db")
  32. if err != nil {
  33. panic("failed to connect database")
  34. }
  35. defer db.Close()
  36. // 启用日志记录,显示详细日志
  37. db.LogMode(true)
  38. // 迁移模式
  39. db.AutoMigrate(&Top{})
  40. db.AutoMigrate(&Middle{})
  41. db.AutoMigrate(&Low{})
  42. db.AutoMigrate(&Bottom{})
  43. top := Top{
  44. Name: "Top",
  45. Middle: []*Middle{
  46. {
  47. Name: "Middle",
  48. Low: []*Low{
  49. {
  50. Name: "Low",
  51. Bottom: []*Bottom{
  52. {
  53. Name: "Bottom",
  54. },
  55. },
  56. },
  57. },
  58. },
  59. },
  60. }
  61. if err := db.Save(&top).Error; err != nil {
  62. log.Fatal("保存计算时出错", err.Error())
  63. }
  64. var ntop Top
  65. if err := db.Where("id = 1").
  66. Preload("Middle.Low.Bottom").
  67. First(&ntop).Error; err != nil {
  68. log.Fatal(err)
  69. }
  70. spew.Dump(&ntop)
  71. }
  1. $ ./main.exe
  2. (C:/Users/fakename/source/stackoverflow/go/gorm-nested/main.go:70)
  3. [2019-10-31 14:44:23] [2.00ms] INSERT INTO "tops" ("name") VALUES ('Top')
  4. [1 rows affected or returned ]
  5. (C:/Users/fakename/source/stackoverflow/go/gorm-nested/main.go:70)
  6. [2019-10-31 14:44:23] [0.00ms] INSERT INTO "middles" ("top_id","name") VALUES (1,'Middle')
  7. [1 rows affected or returned ]
  8. (C:/Users/fakename/source/stackoverflow/go/gorm-nested/main.go:70)
  9. [2019-10-31 14:44:23] [0.99ms] INSERT INTO "lows" ("middle_id","name") VALUES (1,'Low')
  10. [1 rows affected or returned ]
  11. ()
  12. [2019-10-31 14:44:23] [0.00ms] INSERT INTO "bottoms" ("low_id","name") VALUES (1,'Bottom')
  13. [1 rows affected or returned ]
  14. (C:/Users/fakename/source/stackoverflow/go/gorm-nested/main.go:76)
  15. [2019-10-31 14:44:23] [0.00ms] SELECT * FROM "tops" WHERE (id = 1) ORDER BY "tops"."id" ASC LIMIT 1
  16. [1 rows affected or returned ]
  17. (C:/Users/fakename/source/stackoverflow/go/gorm-nested/main.go:76)
  18. [2019-10-31 14:44:23] [0.00ms] SELECT * FROM "middles" WHERE ("top_id" IN (1)) ORDER BY "middles"."id" ASC
  19. [1 rows affected or returned ]
  20. (C:/Users/fakename/source/stackoverflow/go/gorm-nested/main.go:76)
  21. [2019-10-31 14:44:23] [0.00ms] SELECT * FROM "lows" WHERE ("middle_id" IN (1)) ORDER BY "lows"."id" ASC
  22. [1 rows affected or returned ]
  23. (C:/Users/fakename/source/stackoverflow/go/gorm-nested/main.go:76)
  24. [2019-10-31 14:44:23] [0.99ms] SELECT * FROM "bottoms" WHERE ("low_id" IN (1)) ORDER BY "bottoms"."id" ASC
  25. [1 rows affected or returned ]
  26. (*main.Top)(0xc00015f950)({
  27. ID: (uint) 1,
  28. Name: (string) (len=3) "Top",
  29. Middle: ([]*main.Middle) (len=1 cap=1) {
  30. (*main.Middle)(0xc000150fc0)({
  31. ID: (uint) 1,
  32. TopID: (int) 1,
  33. Name: (string) (len=6) "Middle",
  34. Low: ([]*main.Low) (len=1 cap=1) {
  35. (*main.Low)(0xc000151080)({
  36. ID: (uint) 1,
  37. MiddleID: (int) 1,
  38. Name: (string) (len=3) "Low",
  39. Bottom: ([]*main.Bottom) (len=1 cap=1) {
  40. (*main.Bottom)(0xc0001929c0)({
  41. ID: (uint) 1,
  42. LowID: (int) 1,
  43. Name: (string) (len=6) "Bottom"
  44. })
  45. }
  46. })
  47. }
  48. })
  49. }
  50. })
英文:

This issue seems to have been present in earlier versions of gorm as described in: Issue1, Issue2, Issue3, but the lastest preload issue fix has been in this one.

It appears it has been resolved for quite some time but the exact version it was fixed is not documented unless someone searches git commit dates.

Setting up a current example with the latest version (v1.9.11 as of this writing) held no issues.

  1. package main
  2. import (
  3. "log"
  4. "github.com/davecgh/go-spew/spew"
  5. "github.com/jinzhu/gorm"
  6. _ "github.com/jinzhu/gorm/dialects/sqlite"
  7. )
  8. type Top struct {
  9. ID uint `gorm:"primary_key"`
  10. Name string
  11. Middle []*Middle
  12. }
  13. type Middle struct {
  14. ID uint `gorm:"primary_key"`
  15. TopID int
  16. Name string
  17. Low []*Low
  18. }
  19. type Low struct {
  20. ID uint `gorm:"primary_key"`
  21. MiddleID int
  22. Name string
  23. Bottom []*Bottom
  24. }
  25. type Bottom struct {
  26. ID uint `gorm:"primary_key"`
  27. LowID int
  28. Name string
  29. }
  30. func main() {
  31. db, err := gorm.Open("sqlite3", "test.db")
  32. if err != nil {
  33. panic("failed to connect database")
  34. }
  35. defer db.Close()
  36. // Enable Logger, show detailed log
  37. db.LogMode(true)
  38. // Migrate the schema
  39. db.AutoMigrate(&Top{})
  40. db.AutoMigrate(&Middle{})
  41. db.AutoMigrate(&Low{})
  42. db.AutoMigrate(&Bottom{})
  43. top := Top{
  44. Name: "Top",
  45. Middle: []*Middle{
  46. {
  47. Name: "Middle",
  48. Low: []*Low{
  49. {
  50. Name: "Low",
  51. Bottom: []*Bottom{
  52. {
  53. Name: "Bottom",
  54. },
  55. },
  56. },
  57. },
  58. },
  59. },
  60. }
  61. if err := db.Save(&top).Error; err != nil {
  62. log.Fatal("Got errors when saving calc", err.Error())
  63. }
  64. var ntop Top
  65. if err := db.Where("id = 1").
  66. Preload("Middle.Low.Bottom").
  67. First(&ntop).Error; err != nil {
  68. log.Fatal(err)
  69. }
  70. spew.Dump(&ntop)
  71. }
  1. $ ./main.exe
  2. (C:/Users/fakename/source/stackoverflow/go/gorm-nested/main.go:70)
  3. [2019-10-31 14:44:23] [2.00ms] INSERT INTO "tops" ("name") VALUES ('Top')
  4. [1 rows affected or returned ]
  5. (C:/Users/fakename/source/stackoverflow/go/gorm-nested/main.go:70)
  6. [2019-10-31 14:44:23] [0.00ms] INSERT INTO "middles" ("top_id","name") VALUES (1,'Middle')
  7. [1 rows affected or returned ]
  8. (C:/Users/fakename/source/stackoverflow/go/gorm-nested/main.go:70)
  9. [2019-10-31 14:44:23] [0.99ms] INSERT INTO "lows" ("middle_id","name") VALUES (1,'Low')
  10. [1 rows affected or returned ]
  11. ()
  12. [2019-10-31 14:44:23] [0.00ms] INSERT INTO "bottoms" ("low_id","name") VALUES (1,'Bottom')
  13. [1 rows affected or returned ]
  14. (C:/Users/fakename/source/stackoverflow/go/gorm-nested/main.go:76)
  15. [2019-10-31 14:44:23] [0.00ms] SELECT * FROM "tops" WHERE (id = 1) ORDER BY "tops"."id" ASC LIMIT 1
  16. [1 rows affected or returned ]
  17. (C:/Users/fakename/source/stackoverflow/go/gorm-nested/main.go:76)
  18. [2019-10-31 14:44:23] [0.00ms] SELECT * FROM "middles" WHERE ("top_id" IN (1)) ORDER BY "middles"."id" ASC
  19. [1 rows affected or returned ]
  20. (C:/Users/fakename/source/stackoverflow/go/gorm-nested/main.go:76)
  21. [2019-10-31 14:44:23] [0.00ms] SELECT * FROM "lows" WHERE ("middle_id" IN (1)) ORDER BY "lows"."id" ASC
  22. [1 rows affected or returned ]
  23. (C:/Users/fakename/source/stackoverflow/go/gorm-nested/main.go:76)
  24. [2019-10-31 14:44:23] [0.99ms] SELECT * FROM "bottoms" WHERE ("low_id" IN (1)) ORDER BY "bottoms"."id" ASC
  25. [1 rows affected or returned ]
  26. (*main.Top)(0xc00015f950)({
  27. ID: (uint) 1,
  28. Name: (string) (len=3) "Top",
  29. Middle: ([]*main.Middle) (len=1 cap=1) {
  30. (*main.Middle)(0xc000150fc0)({
  31. ID: (uint) 1,
  32. TopID: (int) 1,
  33. Name: (string) (len=6) "Middle",
  34. Low: ([]*main.Low) (len=1 cap=1) {
  35. (*main.Low)(0xc000151080)({
  36. ID: (uint) 1,
  37. MiddleID: (int) 1,
  38. Name: (string) (len=3) "Low",
  39. Bottom: ([]*main.Bottom) (len=1 cap=1) {
  40. (*main.Bottom)(0xc0001929c0)({
  41. ID: (uint) 1,
  42. LowID: (int) 1,
  43. Name: (string) (len=6) "Bottom"
  44. })
  45. }
  46. })
  47. }
  48. })
  49. }
  50. })

huangapple
  • 本文由 发表于 2016年3月18日 09:57:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/36074811.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定