为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
慕工程0101907 2023-06-05 17:44:05
问题在 Golang 中处理一对多或多对多 SQL 关系时,将行映射到结构的最佳(有效、推荐、“Go-like”)方式是什么?采用下面的示例设置,我尝试详细说明一些方法以及每种方法的优缺点,但想知道社区推荐的内容。要求与 PostgreSQL 一起工作(可以是通用的但不包括 MySQL/Oracle 特定功能)效率 - 没有暴力强制每个组合没有 ORM - 理想情况下只使用database/sql和jmoiron/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 []Itemsqlxdb.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,  })}优点一个单一的数据库调用和游标,可以在不消耗太多内存的情况下循环缺点在结构上使用多个连接和许多属性进行复杂且更难开发不太高效;更多的内存使用和处理时间与更多的网络调用
查看完整描述

2 回答

?
慕桂英3389331

TA贡献2036条经验 获得超8个赞

我可以建议我以前使用过的另一种方法。


在这种情况下,您在查询中生成标签的 json 并将其返回。


优点:您有 1 次调用 db,它聚合数据,您所要做的就是将 json 解析为一个数组。


缺点:有点难看。请随意抨击我。


type jointItem struct {

  Item 

  ParsedTags string

  Tags []Tag `gorm:"-"`

}


var jointItems []*jointItem

db.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

}


编辑:代码可能表现得很奇怪,所以我发现在移动时最好使用临时标签数组而不是使用相同的结构。


查看完整回答
反对 回复 2023-06-05
?
慕沐林林

TA贡献2016条经验 获得超9个赞

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



查看完整回答
反对 回复 2023-06-05
?
江户川乱折腾

TA贡献1851条经验 获得超5个赞

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 as

select 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 tags

from 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;

并解组去结构:

亲:

  1. postgres 管理数据的关系。使用 sql 函数添加/更新数据。

  2. golang 管理业务模型和逻辑。

这很简单。


查看完整回答
反对 回复 2023-06-05
  • 2 回答
  • 0 关注
  • 170 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信