关系数据库导致循环

发布于 2025-01-20 16:03:15 字数 6627 浏览 1 评论 0原文

我有以下层次结构,用户 - >地图 - >元素 - >帖子 用户可以拥有一堆地图,每个地图都有许多元素,每个元素都会有许多帖子。

类型用户struct {

UserID        uint   `gorm:"primarykey;autoIncrement;not_null"`
UserName string `json: user_name`
FirstName string `json: first_name`
LastName  string `json: last_name`
Email     string `json:email`
Password  []byte `json:"-"`
Phone     string `json:"phone"`
Maps []Map `gorm:"-"`

}

type map struct {

MapID   uint      `gorm:"primarykey;autoIncrement;not_null"`
UserID   uint    `json:userid`
User     User      `json:"user"; gorm:"foreignkey:UserID`
Title    string    `json:title`
Desc     string    `json: "desc"`
Elements []Element `gorm:"foreignKey:MapID"`
Date     time.Time `json: date`

}

类型元素cint {

ElementID   uint       `gorm:"primarykey;autoIncrement;not_null"`
ElementName string     `json: element_name`
Desc        string     `json: desc`
MapID uint `json:mapid`
Map   Map   `json:"map"; gorm:"foreignkey:MapID`
Posts       []Post     `gorm:"foreignKey:ElementID"`
Date        time.Time  `json: date`
UserID uint `json:userid`
User   User   `json:"user"; gorm:"foreignkey:UserID`

}

类型post struct {

PostId    uint      `gorm:"primarykey;autoIncrement;not_null"`
Title     string    `json: p_title`
Subject   string    `json: subject`
Date      time.Time `json: date`
Entry     string    `json: entry_text`
ElementID uint `json:elementid`
Element   Element   `json:"element"; gorm:"foreignkey:ElementID`
UserID uint `json:userid`
User   User   `json:"user"; gorm:"foreignkey:UserID`

}

这一切似乎都很好,但是现在,当我从后端发送JSON响应时,似乎有无限循环的潜力。

当我检索所有用户的地图时,它列出了与创建地图的用户相关的用户对象,但是映射还包括一个元素列表,在元素对象中,它将列出其属于和的地图和该地图对象将再次列出所有元素。

那么,我应该只是在一个方向上预紧层次结构来处理此问题吗?

var getMaps []型号 database.db.preload(“ user”).preload(“ map”)。preload(“ elements”)。offset(offset).limit(limit).find(& getMaps),

或者我应该修复struct和gorm设置仅在一个方向上建立关系?由于返回地图将返回其元素,每个元素将返回其属于其循环回到其元素等的地图。

此循环也将在元素和元素上有很多帖子的元素和帖子中发生,这些帖子对象将显示其元素该元素对象将显示其帖子。

我敢肯定,有一种理想或最佳的实施方法,因此只要好奇人们会推荐什么。

示例调用一个带有以下预加载

func DetailMap(c *fiber.Ctx) error {
    id,_ := strconv.Atoi(c.Params("id"))
    fmt.Println(id)
    var smap models.Map
    database.DB.Where("map_id=?", id).Preload("User").Preload("Map").Preload("Elements.Posts").First(&smap)
    return c.JSON(fiber.Map{
        "data":smap,
    })

}

“数据”的地图:{

    "MapID": 1,
    "UserID": 1,
    "user": {
        "UserID": 1,
        "UserName": "Chris",
        "FirstName": "Chris",
        "LastName": "XxxXxxxx",
        "Email": "[email protected]",
        "phone": "123-456-6789",
        "Maps": null
    },
    "Title": "My Map",
    "Desc": "This is the subject",
    "Elements": [
        {
            "ElementID": 1,
            "ElementType": "BASE",
            "ElementName": "Identity",
            "BriefDesc": "This is the identity ",
            "Desc": "In publishing and graphic design
            "ParentId": "",
            "NumElements": 0,
            "NumEntries": 0,
            "MapID": 1,
            "map": {
                "MapID": 0,
                "UserID": 0,
                "user": {
                    "UserID": 0,
                    "UserName": "",
                    "FirstName": "",
                    "LastName": "",
                    "Email": "",
                    "phone": "",
                    "Maps": null
                },
                "Title": "",
                "Desc": "",
                "Elements": null,
                "Date": "0001-01-01T00:00:00Z"
            },
            "Notes": null,
            "Questions": null,
            "Posts": [
                {
                    "PostId": 1,
                    "Title": "First Post",
                    "Subject": "This is the subject",
                    "Date": "2022-04-11T12:35:55.267-03:00",
                    "Entry": "This is the Entry",
                    "ElementID": 1,
                    "element": {
                        "ElementID": 0,
                        "ElementType": "",
                        "ElementName": "",
                        "BriefDesc": "",
                        "Desc": "",
                        "ParentId": "",
                        "NumElements": 0,
                        "NumEntries": 0,
                        "MapID": 0,
                        "map": {
                            "MapID": 0,
                            "UserID": 0,
                            "user": {
                                "UserID": 0,
                                "UserName": "",
                                "FirstName": "",
                                "LastName": "",
                                "Email": "",
                                "phone": "",
                                "Maps": null
                            },
                            "Title": "",
                            "Desc": "",
                            "Elements": null,
                            "Date": "0001-01-01T00:00:00Z"
                        },
                        "Notes": null,
                        "Questions": null,
                        "Posts": null,
                        "Date": "0001-01-01T00:00:00Z",
                        "UserID": 0,
                        "user": {
                            "UserID": 0,
                            "UserName": "",
                            "FirstName": "",
                            "LastName": "",
                            "Email": "",
                            "phone": "",
                            "Maps": null
                        }
                    },
                    "UserID": 1,
                    "user": {
                        "UserID": 0,
                        "UserName": "",
                        "FirstName": "",
                        "LastName": "",
                        "Email": "",
                        "phone": "",
                        "Maps": null
                    }
                }
            ],
            "Date": "2022-04-11T11:31:01.72-03:00",
            "UserID": 1,
            "user": {
                "UserID": 0,
                "UserName": "",
                "FirstName": "",
                "LastName": "",
                "Email": "",
                "phone": "",
                "Maps": null
            }
        },`

I have the following hierarchy, Users -> Maps -> Elements -> Posts
A user can have a bunch of maps, each map will have a number of elements and each element will have a number of posts.

type User struct {

UserID        uint   `gorm:"primarykey;autoIncrement;not_null"`
UserName string `json: user_name`
FirstName string `json: first_name`
LastName  string `json: last_name`
Email     string `json:email`
Password  []byte `json:"-"`
Phone     string `json:"phone"`
Maps []Map `gorm:"-"`

}

type Map struct {

MapID   uint      `gorm:"primarykey;autoIncrement;not_null"`
UserID   uint    `json:userid`
User     User      `json:"user"; gorm:"foreignkey:UserID`
Title    string    `json:title`
Desc     string    `json: "desc"`
Elements []Element `gorm:"foreignKey:MapID"`
Date     time.Time `json: date`

}

type Element struct {

ElementID   uint       `gorm:"primarykey;autoIncrement;not_null"`
ElementName string     `json: element_name`
Desc        string     `json: desc`
MapID uint `json:mapid`
Map   Map   `json:"map"; gorm:"foreignkey:MapID`
Posts       []Post     `gorm:"foreignKey:ElementID"`
Date        time.Time  `json: date`
UserID uint `json:userid`
User   User   `json:"user"; gorm:"foreignkey:UserID`

}

type Post struct {

PostId    uint      `gorm:"primarykey;autoIncrement;not_null"`
Title     string    `json: p_title`
Subject   string    `json: subject`
Date      time.Time `json: date`
Entry     string    `json: entry_text`
ElementID uint `json:elementid`
Element   Element   `json:"element"; gorm:"foreignkey:ElementID`
UserID uint `json:userid`
User   User   `json:"user"; gorm:"foreignkey:UserID`

}

This all seems to work fine, but now when I send the JSON response from the backend there seems to be potential for an infinite loop.

When I retrieve all of a user's maps, it then lists the user object relating to the user that created the map, but the map then also includes a list of elements and within the element object it is going to list the map it belongs to and that map object will again list all of it's elements.

So should I be handling this by just preloading the hierarchy in one direction?

var getmaps []models.Map
database.DB.Preload("User").Preload("Map").Preload("Elements").Offset(offset).Limit(limit).Find(&getmaps)

Or should I be fixing the struct and gorm settings to only get relationships in one direction? Since returning a map will return it's elements and each element will return the map it belongs to which loops back to its elements etc.

This loop would also happen with Elements and posts where an element will have many posts, those post objects would display their element and that element object would display its posts.

I'm sure there is an ideal or optimum way to implement this so just curious what people would recommend.

Example calling one map with the following preloads

func DetailMap(c *fiber.Ctx) error {
    id,_ := strconv.Atoi(c.Params("id"))
    fmt.Println(id)
    var smap models.Map
    database.DB.Where("map_id=?", id).Preload("User").Preload("Map").Preload("Elements.Posts").First(&smap)
    return c.JSON(fiber.Map{
        "data":smap,
    })

}

"data": {

    "MapID": 1,
    "UserID": 1,
    "user": {
        "UserID": 1,
        "UserName": "Chris",
        "FirstName": "Chris",
        "LastName": "XxxXxxxx",
        "Email": "[email protected]",
        "phone": "123-456-6789",
        "Maps": null
    },
    "Title": "My Map",
    "Desc": "This is the subject",
    "Elements": [
        {
            "ElementID": 1,
            "ElementType": "BASE",
            "ElementName": "Identity",
            "BriefDesc": "This is the identity ",
            "Desc": "In publishing and graphic design
            "ParentId": "",
            "NumElements": 0,
            "NumEntries": 0,
            "MapID": 1,
            "map": {
                "MapID": 0,
                "UserID": 0,
                "user": {
                    "UserID": 0,
                    "UserName": "",
                    "FirstName": "",
                    "LastName": "",
                    "Email": "",
                    "phone": "",
                    "Maps": null
                },
                "Title": "",
                "Desc": "",
                "Elements": null,
                "Date": "0001-01-01T00:00:00Z"
            },
            "Notes": null,
            "Questions": null,
            "Posts": [
                {
                    "PostId": 1,
                    "Title": "First Post",
                    "Subject": "This is the subject",
                    "Date": "2022-04-11T12:35:55.267-03:00",
                    "Entry": "This is the Entry",
                    "ElementID": 1,
                    "element": {
                        "ElementID": 0,
                        "ElementType": "",
                        "ElementName": "",
                        "BriefDesc": "",
                        "Desc": "",
                        "ParentId": "",
                        "NumElements": 0,
                        "NumEntries": 0,
                        "MapID": 0,
                        "map": {
                            "MapID": 0,
                            "UserID": 0,
                            "user": {
                                "UserID": 0,
                                "UserName": "",
                                "FirstName": "",
                                "LastName": "",
                                "Email": "",
                                "phone": "",
                                "Maps": null
                            },
                            "Title": "",
                            "Desc": "",
                            "Elements": null,
                            "Date": "0001-01-01T00:00:00Z"
                        },
                        "Notes": null,
                        "Questions": null,
                        "Posts": null,
                        "Date": "0001-01-01T00:00:00Z",
                        "UserID": 0,
                        "user": {
                            "UserID": 0,
                            "UserName": "",
                            "FirstName": "",
                            "LastName": "",
                            "Email": "",
                            "phone": "",
                            "Maps": null
                        }
                    },
                    "UserID": 1,
                    "user": {
                        "UserID": 0,
                        "UserName": "",
                        "FirstName": "",
                        "LastName": "",
                        "Email": "",
                        "phone": "",
                        "Maps": null
                    }
                }
            ],
            "Date": "2022-04-11T11:31:01.72-03:00",
            "UserID": 1,
            "user": {
                "UserID": 0,
                "UserName": "",
                "FirstName": "",
                "LastName": "",
                "Email": "",
                "phone": "",
                "Maps": null
            }
        },`

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

将军与妓 2025-01-27 16:03:15

在您的 PostMapElement 结构中,您有以下字段:

UserID uint   `json:userid`
User   User   `json:"user"; gorm:"foreignkey:UserID`

您应该从内容中删除 User 字段结构,因为您已经有一个 UserID。在这种情况下,“引用”(ID)比包含整个用户对象更明智。客户端可以调用 /users/{id} 端点并根据需要查找更多信息。

还可以通过删除 Maps []Map (负责您提到的循环)来限制 User 结构的内容。然后,您需要设置 /user/{id}/maps 等端点,以便客户端可以获得用户的内容。

这同样适用于 PostElement。您可以全力以赴并仅存储 ID,也可以存储一组仅“子”模型。 (地图嵌入元素,元素不​​嵌入地图)。因此,要查找元素的关联地图,您可以调用端点 /maps/{您元素的地图 ID}。元素 > 相同 为了避免循环,

type Map struct {
    gorm.Model // this takes care of the ID field
    UserID   uint    `json:userid`
    Title    string    `json:title`
    Desc     string    `json: "desc"`
    Elements []Element // gorm will handle the relationship automatically
    Date     time.Time `json: date`
}
type Element struct {
gorm.Model // includes ID
ElementName string     `json: element_name`
Desc        string     `json: desc`
MapID uint `json:mapid`
// Map   Map  ... This relationship is described by another endpoint - /elements/{elementId}/map to get the related map

Posts       []Post     // gorm handles this
Date        time.Time  `json: date`
UserID uint `json:userid`
}
type Post struct {
    gorm.Model
    Title     string    `json: p_title`
    Subject   string    `json: subject`
    Date      time.Time `json: date`
    Entry     string    `json: entry_text`
    ElementID uint `json:elementid` // gorm will use this as fk
    UserID uint `json:userid`
}

您需要在结构级别使关系成为单向,并设置更多的 http 路由以走向另一个方向(请参阅注释代码)。

我描述的是一个简单的 REST api。微软有一个很好的概述:https://learn.microsoft.com/en-us/azure/architecture/best-practices/api-design#organize-the-api-design-around-resources - 特别是您会对客户/订单关系感兴趣。

在 gorm 方面,您将使用一对多关联: https://gorm.io/docs /has_many.html

In your Post, Map and Element structs you have the fields:

UserID uint   `json:userid`
User   User   `json:"user"; gorm:"foreignkey:UserID`

You should remove the User field from your content structs because you already have a UserID. A "reference" (ID) in this case is more sensible than including the whole user object. The client can call a /users/{id} endpoint and find more info if needed.

Also limit the content of the User struct by removing Maps []Map (responsible for the loop you mentioned). You would then need to set up endpoints like /user/{id}/maps so the client can get the user's content.

The same applies for Post and Element. You could go all-out and store only IDs, or you can store an array of only "child" models. (Map embeds Element, Element DOES NOT embed Map). So to find the associated map of an element, you would call endpoint /maps/{your element's map ID}. same for Element > Post

type Map struct {
    gorm.Model // this takes care of the ID field
    UserID   uint    `json:userid`
    Title    string    `json:title`
    Desc     string    `json: "desc"`
    Elements []Element // gorm will handle the relationship automatically
    Date     time.Time `json: date`
}
type Element struct {
gorm.Model // includes ID
ElementName string     `json: element_name`
Desc        string     `json: desc`
MapID uint `json:mapid`
// Map   Map  ... This relationship is described by another endpoint - /elements/{elementId}/map to get the related map

Posts       []Post     // gorm handles this
Date        time.Time  `json: date`
UserID uint `json:userid`
}
type Post struct {
    gorm.Model
    Title     string    `json: p_title`
    Subject   string    `json: subject`
    Date      time.Time `json: date`
    Entry     string    `json: entry_text`
    ElementID uint `json:elementid` // gorm will use this as fk
    UserID uint `json:userid`
}

To avoid loops you will need to make the relationships one-directional at a struct level, and set up more http routes to go in the other direction (see commented code).

What I described is a simple REST api. Microsoft has a nice overview: https://learn.microsoft.com/en-us/azure/architecture/best-practices/api-design#organize-the-api-design-around-resources - specifically the customer/order relationship will interest you.

On the gorm side you would be using one-to-many associations: https://gorm.io/docs/has_many.html

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文