关系数据库导致循环

Relational Database Resulting in Loop

我有以下层次结构,用户 -> 地图 -> 元素 -> Posts 一个用户可以有一堆地图,每个地图都会有一些元素,每个元素会有一些 posts.

类型用户结构{

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:"-"`

}

类型地图结构{

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`

}

类型元素结构{

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结构{

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[]models.Map database.DB.Preload("User").Preload("Map").Preload("Elements").Offset(offset).Limit(limit).Find(&getmaps)

或者我应该修复 struct 和 gorm 设置以仅在一个方向上获得关系?因为 return 地图将 return 它的元素和每个元素将 return 它所属的地图循环回到它的元素等

这个循环也会发生在元素和 posts 中,其中一个元素将有许多 posts,那些 post 对象将显示它们的元素,而该元素对象将显示它的posts.

我确信有一个理想或最佳的方法来实现这个,所以很好奇人们会推荐什么。

使用以下预加载调用一张地图的示例

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": "xxxxx@gmail.com",
        "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
            }
        },`

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

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

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

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

PostElement也是如此。您可以选择 all-out 并仅存储 ID,或者您可以存储仅包含“子”模型的数组。 (地图嵌入元素,元素不​​嵌入地图)。因此,要查找元素的关联映射,您可以调用端点 /maps/{your element's map ID}。元素相同 > 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`
}

为避免循环,您需要在结构级别建立关系 one-directional,并设置更多的 http 路由以朝另一个方向发展(参见注释代码)。

我描述的是一个简单的 REST api。 Microsoft 有一个很好的概述:https://docs.microsoft.com/en-us/azure/architecture/best-practices/api-design#organize-the-api-design-around-resources - 特别是 customer/order 关系会让您感兴趣。

在 gorm 方面,您将使用 one-to-many 关联:https://gorm.io/docs/has_many.html