关系数据库导致循环
我有以下层次结构,用户 - >地图 - >元素 - >帖子 用户可以拥有一堆地图,每个地图都有许多元素,每个元素都会有许多帖子。
类型用户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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
在您的
Post
、Map
和Element
结构中,您有以下字段:您应该从内容中删除
User
字段结构,因为您已经有一个UserID
。在这种情况下,“引用”(ID)比包含整个用户对象更明智。客户端可以调用/users/{id}
端点并根据需要查找更多信息。还可以通过删除
Maps []Map
(负责您提到的循环)来限制User
结构的内容。然后,您需要设置/user/{id}/maps
等端点,以便客户端可以获得用户的内容。这同样适用于
Post
和Element
。您可以全力以赴并仅存储 ID,也可以存储一组仅“子”模型。 (地图嵌入元素,元素不嵌入地图)。因此,要查找元素的关联地图,您可以调用端点/maps/{您元素的地图 ID}
。元素 > 相同 为了避免循环,您需要在结构级别使关系成为单向,并设置更多的 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
andElement
structs you have the fields:You should remove the
User
field from your content structs because you already have aUserID
. 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 removingMaps []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
andElement
. 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 > PostTo 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