有效地将一对多多对多数据库映射到 Golang 中的结构

问题

在 Golang 中处理一对多或多对多 SQL 关系时,将行映射到结构的最佳(有效、推荐、“Go-like”)方式是什么?

采用下面的示例设置,我尝试详细说明一些方法以及每种方法的优缺点,但想知道社区推荐的内容。

要求

  • 与 PostgreSQL 一起工作(可以是通用的但不包括 MySQL/Oracle 特定功能)

  • 效率 - 没有暴力强制每个组合

  • 没有 ORM - 理想情况下只使用database/sqljmoiron/sqlx

例子

为了清楚起见,我删除了错误处理

楷模

type Tag struct {

  ID int

  Name string

}


type Item struct {

  ID int

  Tags []Tag

}

数据库


CREATE TABLE item (

  id                      INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY

);


CREATE TABLE tag (

  id                      INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,

  name                    VARCHAR(160),

  item_id                 INT REFERENCES item(id)

);

方法 1 - 选择所有项目,然后为每个项目选择标签


var items []Item

sqlxdb.Select(&items, "SELECT * FROM item")


for i, item := range items {

  var tags []Tag

  sqlxdb.Select(&tags, "SELECT * FROM tag WHERE item_id = $1", item.ID)

  items[i].Tags = tags

}

优点

  • 简单的

  • 容易明白

缺点

  • 数据库查询数量与项目数量成比例增加时效率低下

方法 2 - 手动构建 SQL 连接并遍历行

var itemTags = make(map[int][]Tag)


var items = []Item{}

rows, _ := sqlxdb.Queryx("SELECT i.id, t.id, t.name FROM item AS i JOIN tag AS t ON t.item_id = i.id")

for rows.Next() {

  var (

    itemID  int

    tagID   int

    tagName string

  )

  rows.Scan(&itemID, &tagID, &tagName)

  if tags, ok := itemTags[itemID]; ok {

    itemTags[itemID] = append(tags, Tag{ID: tagID, Name: tagName,})

  } else {

    itemTags[itemID] = []Tag{Tag{ID: tagID, Name: tagName,}}

  }

}

for itemID, tags := range itemTags {

  items = append(Item{

    ID: itemID,

    Tags: tags,

  })

}

优点

  • 一个单一的数据库调用和游标,可以在不消耗太多内存的情况下循环

缺点

  • 在结构上使用多个连接和许多属性进行复杂且更难开发

  • 不太高效;更多的内存使用和处理时间与更多的网络调用


慕工程0101907
浏览 171回答 2
2回答

慕桂英3389331

我可以建议我以前使用过的另一种方法。在这种情况下,您在查询中生成标签的 json 并将其返回。优点:您有 1 次调用 db,它聚合数据,您所要做的就是将 json 解析为一个数组。缺点:有点难看。请随意抨击我。type jointItem struct {  Item   ParsedTags string  Tags []Tag `gorm:"-"`}var jointItems []*jointItemdb.Raw(`SELECT   items.*,   (SELECT CONCAT(            '[',              GROUP_CONCAT(                  JSON_OBJECT('id', id,                             'name', name                   )             ),             ']'         )) as parsed_tags    FROM items`).Scan(&jointItems)for _, o := range jointItems {var tempTags []Tag   if err := json.Unmarshall(o.ParsedTags, &tempTags) ; err != nil {      // do something   }  o.Tags = tempTags}编辑:代码可能表现得很奇怪,所以我发现在移动时最好使用临时标签数组而不是使用相同的结构。

慕沐林林

您可以使用来自https://github.com/jackskj/carta的 carta.Map() 它会自动跟踪有很多关系。

江户川乱折腾

postgres 中的 sql:create schema temp;set search_path = temp;create table item(  id INT generated by default as identity primary key);create table tag(  id      INT generated by default as identity primary key,  name    VARCHAR(160),  item_id INT references item (id));create view item_tags asselect id,  (          select            array_to_json(array_agg(row_to_json(taglist.*))) as array_to_json          from (                select tag.name, tag.id                 from tag                         where item_id = item.id               ) taglist ) as tagsfrom item ;-- golang query this maybe select  row_to_json(row)from (    select * from item_tags) row;然后golang查询这个sql:select  row_to_json(row)from (    select * from item_tags) row;并解组去结构:亲:postgres 管理数据的关系。使用 sql 函数添加/更新数据。golang 管理业务模型和逻辑。这很简单。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go