指定`gorm:”unique”`会导致预加载属于关系的问题。

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

Specifying `gorm:"unique"`, causing preload of belongs to relation a problem

问题

这是我的帖子和用户结构模型:

  1. type Post struct {
  2. ID string `gorm:"primaryKey"`
  3. Title string `gorm:"unique"`
  4. UserID string
  5. User User
  6. }
  7. type User struct {
  8. ID string `gorm:"primaryKey"`
  9. Name string
  10. }
  11. // 创建一个具有标题和用户输入的帖子的代码
  12. func CreatePost(title string, u User) (Post, error) {
  13. b := Post{Title: title, User: u}
  14. dbIns := GetDB()
  15. if err := dbIns.Create(&b).Error; err != nil {
  16. return Post{}, err
  17. }
  18. return b, nil
  19. }
  20. // 获取帖子的代码,同时预加载其用户字段
  21. func GetOnePost() (Post, error) {
  22. var p Post
  23. dbIns := GetDB()
  24. if err := dbIns.Model(&Post{}).Preload("User").First(&p).Error; err != nil {
  25. return Post{}, err
  26. }
  27. return p, nil
  28. }

这是从GetOnePost()获得的输出,正是我所期望的,Post.User不为空。

  1. 2022/11/27 15:42:48 帖子标题John-Doe的标题UUID32158510-6dcc-4c26-bcb8-7d76a88bda72作者UUID73d020d3-eb8d-4bf7-9f57-e821c5deceed
  2. 2022/11/27 15:42:48 用户名John-DoeUUID73d020d3-eb8d-4bf7-9f57-e821c5deceed

然而,当我将User.Name指定为唯一时(因为我希望每个用户具有不同的名称),GetOnePost()的输出发生了变化。请忽略UUID。

  1. 2022/11/27 15:57:49 帖子标题John-Doe的标题UUID26355816-c376-497a-a250-be118c5933e2作者UUIDe858cd72-56b9-4884-b6e6-94121c36719b
  2. 2022/11/27 15:57:49 用户名:,UUID

问题在于,当我向User模型添加唯一约束时,返回的Post.User似乎为空。如果我从User模型中删除唯一约束,它就恢复正常(Post.User非空)。所以我认为问题出在gorm:"unique"这部分。

英文:

Here's my Post & User struct model :

  1. type Post struct {
  2. ID string `gorm:"primaryKey"`
  3. Title string `gorm:"unique"`
  4. UserID string
  5. User User
  6. }
  7. type User struct {
  8. ID string `gorm:"primaryKey"`
  9. Name string
  10. }
  11. // Code to create a Post with title & user input
  12. func CreatePost(title string, u User) (Post, error) {
  13. b := Post{Title: title, User: u}
  14. dbIns := GetDB()
  15. if err := dbIns.Create(&b).Error; err != nil {
  16. return Post{}, err
  17. }
  18. return b, nil
  19. }
  20. // Code to get a Post, also preload its User field
  21. func GetOnePost() (Post, error) {
  22. var p Post
  23. dbIns := GetDB()
  24. if err := dbIns.Model(&Post{}).Preload("User").First(&p).Error; err != nil {
  25. return Post{}, err
  26. }
  27. return p, nil
  28. }

Here's is an output of what I got from GetOnePost(), which is exactly what I expected, Post.User is not empty.

  1. 2022/11/27 15:42:48 Post title: John-Doe's Title, UUID: 32158510-6dcc-4c26-bcb8-7d76a88bda72, authorUUID: 73d020d3-eb8d-4bf7-9f57-e821c5deceed
  2. 2022/11/27 15:42:48 User name: John-Doe, UUID: 73d020d3-eb8d-4bf7-9f57-e821c5deceed

However when I specified the User.Name to be unique (since I want each user to have different Name)<br/>
note : yes I did migrate into a new table.

  1. type User struct {
  2. ID string `gorm:&quot;primaryKey&quot;`
  3. Name string `gorm:&quot;unique&quot;`
  4. }

The output from GetOnePost() changed to this : (please ignore the UUID)

  1. 2022/11/27 15:57:49 Post title: John-Doe&#39;s Title, UUID: 26355816-c376-497a-a250-be118c5933e2, authorUUID: e858cd72-56b9-4884-b6e6-94121c36719b
  2. 2022/11/27 15:57:49 User name: , UUID:

A problem is that the returned Post.User seems to be empty, when I add unique constraint to User model.

If I remove the unique constraint from User model, it returns to normal (Post.User not-empty). So it seems to me that the problem is with the gorm:"unique" part.

答案1

得分: 1

老实说,我无法重现你的问题。不过,我添加了我尝试重现你的问题的代码,它按预期工作。首先让我展示代码,然后我会给你一个简要的解释:

  1. package main
  2. import (
  3. "fmt"
  4. "gorm.io/driver/postgres"
  5. "gorm.io/gorm"
  6. "github.com/google/uuid"
  7. _ "github.com/lib/pq"
  8. )
  9. type Post struct {
  10. ID string `gorm:"primaryKey"`
  11. Title string `gorm:"unique"`
  12. UserID string
  13. User User
  14. }
  15. type User struct {
  16. ID string `gorm:"primaryKey"`
  17. Name string `gorm:"unique"`
  18. }
  19. // 创建一个带有标题和用户输入的帖子的代码
  20. func CreatePost(db *gorm.DB, title string, u User) (Post, error) {
  21. id := uuid.NewString()
  22. b := Post{ID: id, Title: title, User: u}
  23. if err := db.Create(&b).Error; err != nil {
  24. return Post{}, err
  25. }
  26. return b, nil
  27. }
  28. // 获取一篇帖子的代码,同时预加载其User字段
  29. func GetOnePost(db *gorm.DB) (Post, error) {
  30. var p Post
  31. if err := db.Model(&Post{}).Preload("User").First(&p).Error; err != nil {
  32. return Post{}, err
  33. }
  34. return p, nil
  35. }
  36. func main() {
  37. dsn := "host=localhost user=postgres password=postgres dbname=postgres port=5432 sslmode=disable"
  38. db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
  39. if err != nil {
  40. panic(err)
  41. }
  42. db.AutoMigrate(&User{})
  43. db.AutoMigrate(&Post{})
  44. defer db.Exec("DROP TABLE users")
  45. defer db.Exec("DROP TABLE posts")
  46. userId := uuid.NewString()
  47. _, err = CreatePost(db, "Learn Go", User{
  48. ID: userId,
  49. Name: "John Doe",
  50. })
  51. if err != nil {
  52. panic(err)
  53. }
  54. post, err := GetOnePost(db)
  55. if err != nil {
  56. panic(err)
  57. }
  58. // 这会导致数据库中的重复错误 SQLSTATE 23505
  59. // if err := db.Create(&User{
  60. // ID: uuid.NewString(),
  61. // Name: "John Doe",
  62. // }).Error; err != nil {
  63. // panic(err)
  64. // }
  65. fmt.Printf("帖子的详细信息:%v\n", post)
  66. }

代码基本上与你提供的代码相同。我添加了两个延迟函数调用,以便在每次运行代码后删除表。通过这样做,我们始终确保使用最新的表定义。然后,我在Postgres中检查了UNIQUE约束是否存在,并且我正确地找到了它。然而,当我尝试运行我的代码时,我成功地能够检索到User信息。如果你尝试取消注释以// this leads to a Duplicate error SQLSTATE 23505 in DB开头的代码块,你将会得到一个重复错误,因此约束在数据库端得到了执行。

如果你使用我的解决方案,让我知道你的代码是否也能正常工作!

英文:

Honestly, I was not able to repro your issue. Anyway, I added my attempt to repro your issue that works as expected. Let me show the code first and then I'll provide you with a brief explanation:

  1. package main
  2. import (
  3. &quot;fmt&quot;
  4. &quot;gorm.io/driver/postgres&quot;
  5. &quot;gorm.io/gorm&quot;
  6. &quot;github.com/google/uuid&quot;
  7. _ &quot;github.com/lib/pq&quot;
  8. )
  9. type Post struct {
  10. ID string `gorm:&quot;primaryKey&quot;`
  11. Title string `gorm:&quot;unique&quot;`
  12. UserID string
  13. User User
  14. }
  15. type User struct {
  16. ID string `gorm:&quot;primaryKey&quot;`
  17. Name string `gorm:&quot;unique&quot;`
  18. }
  19. // Code to create a Post with title &amp; user input
  20. func CreatePost(db *gorm.DB, title string, u User) (Post, error) {
  21. id := uuid.NewString()
  22. b := Post{ID: id, Title: title, User: u}
  23. if err := db.Create(&amp;b).Error; err != nil {
  24. return Post{}, err
  25. }
  26. return b, nil
  27. }
  28. // Code to get a Post, also preload its User field
  29. func GetOnePost(db *gorm.DB) (Post, error) {
  30. var p Post
  31. if err := db.Model(&amp;Post{}).Preload(&quot;User&quot;).First(&amp;p).Error; err != nil {
  32. return Post{}, err
  33. }
  34. return p, nil
  35. }
  36. func main() {
  37. dsn := &quot;host=localhost user=postgres password=postgres dbname=postgres port=5432 sslmode=disable&quot;
  38. db, err := gorm.Open(postgres.Open(dsn), &amp;gorm.Config{})
  39. if err != nil {
  40. panic(err)
  41. }
  42. db.AutoMigrate(&amp;User{})
  43. db.AutoMigrate(&amp;Post{})
  44. defer db.Exec(&quot;DROP TABLE users&quot;)
  45. defer db.Exec(&quot;DROP TABLE posts&quot;)
  46. userId := uuid.NewString()
  47. _, err = CreatePost(db, &quot;Learn Go&quot;, User{
  48. ID: userId,
  49. Name: &quot;John Doe&quot;,
  50. })
  51. if err != nil {
  52. panic(err)
  53. }
  54. post, err := GetOnePost(db)
  55. if err != nil {
  56. panic(err)
  57. }
  58. // this leads to a Duplicate error SQLSTATE 23505 in DB
  59. // if err := db.Create(&amp;User{
  60. // ID: uuid.NewString(),
  61. // Name: &quot;John Doe&quot;,
  62. // }).Error; err != nil {
  63. // panic(err)
  64. // }
  65. fmt.Printf(&quot;post&#39;s details: %v\n&quot;, post)
  66. }

More or less the code is the same as you provided. I added two deferred function calls to delete the tables after every run of the code. Thanks to this we're always sure to work with the latest table definition provided. Then, I check in Postgres if the UNIQUE constraint was present and I correctly found it. However, when I try to run my code I was successfully able to retrieve also the User information. If you try to uncomment the code block starting with this comment // this leads to a Duplicate error SQLSTATE 23505 in DB you'll get a duplicate error so the constraint is enforced on the database side.

Let me know if by using my solution your code works too!

huangapple
  • 本文由 发表于 2022年11月27日 11:06:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/74587204.html
匿名

发表评论

匿名网友

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

确定