【问题标题】:Golang gorm - Get relation without using preloadGolang gorm - 在不使用预加载的情况下获取关系
【发布时间】:2021-03-25 09:06:22
【问题描述】:

我是 gorm 和 golang 的新手,我正在处理一个查询,以获取用户与对话中用户的对话。我的查询按预期工作,但我的问题是它没有带来用户的关系。

这是我的对话模型:

type Conversation struct {
    ID       uint   `gorm:"primarykey"`
    Users    []User `gorm:"many2many:user_has_conversations;"`
    Messages []ConversationMessage
}

这是我对数据库的要求:

var conversations []model.Conversation

result := r.db.Debug().Scopes(
        pagination.Paginate,
        scope.OrderByIDDescTable(conversationsTable),
    ).Joins(
        "INNER JOIN `user_has_conversations` ON `user_has_conversations`.`conversation_id` = `conversations`.`id`",
    ).Joins(
        "INNER JOIN `users` ON `users`.`id` = `user_has_conversations`.`user_id` AND `users`.`id` = ?",
        UserID,
    ).Find(&conversations)

此查询工作正常,并且正确地带来了用户的对话,但没有将关系中的用户归还给我,并且当您查看它运行的查询时,它不会带来用户字段。

执行的查询:

SELECT `conversations`.`id` 
FROM `conversations` 
INNER JOIN `user_has_conversations` ON `user_has_conversations`.`conversation_id` = `conversations`.`id` 
INNER JOIN `users` ON `users`.`id` = `user_has_conversations`.`user_id` AND `users`.`id` = 1 
ORDER BY `conversations`.`id` desc LIMIT 10;

在查询中可以看到查询是正确的,但是没有Select与关系的字段。

如果我在内部连接中也使用预加载确实给了我正确的解决方案,但我认为当我已经加入来对用户进行预加载以再次获得用户时,这对性能没有好处。我希望能够通过加入获得它,而不是使用预加载。

这个例子有效,但如果有办法,我想在没有预加载的情况下使用它。

result := r.db.Debug().Scopes(
        pagination.Paginate,
        scope.OrderByIDDescTable(conversationsTable),
    ).Preload("Users").Joins(
        "INNER JOIN `user_has_conversations` ON `user_has_conversations`.`conversation_id` = `conversations`.`id`",
    ).Joins(
        "INNER JOIN `users` ON `users`.`id` = `user_has_conversations`.`user_id` AND `users`.`id` = ?",
        UserID,
    ).Find(&conversations)

【问题讨论】:

    标签: sql go go-gorm


    【解决方案1】:

    您可以通过为 gorm 实现自定义数据类型并在查询中使用它来从数据库中接收 users 切片(如下所示)。

    自定义数据类型必须实现 Scanner 和 Valuer 接口,因此 GORM 知道如何将其接收/保存到数据库中。 https://gorm.io/docs/data_types.html

    type Users []User
    
    func (u Users) Value() (driver.Value, error) {
        return json.Marshal(u)
    }
    
    func (u *Users) Scan(src interface{}) error {
        if bytes, ok := src.([]byte); ok {
            return json.Unmarshal(bytes, u)
        }
        return errors.New(fmt.Sprint("Failed to unmarshal JSON from DB", src))
    }
    

    所以你的conversation 类型应该如下更新:

    type Conversation struct {
        ID       uint  `gorm:"primarykey"`
        Users    Users `gorm:"-"`
        Messages []ConversationMessage
    }
    

    为了能够查询对话中所有参与的用户,我们需要使用 jsonb_agg 和 jsonb_build_object 构建 json 对象

    var conversations []model.Conversation
    
    result := r.db.Debug().Scopes(
            pagination.Paginate,
            scope.OrderByIDDescTable(conversationsTable),
        ).
        Select(conversationsTable + ".*," +
        "jsonb_agg(
            distinct jsonb_build_object(
                'id',   u.id,
                'name', u.name,
                //...
            )
        ) as users").
        Joins("INNER JOIN `user_has_conversations` ON `user_has_conversations`.`conversation_id` = `conversations`.`id`").
        Joins("INNER JOIN `users` as u ON `u`.`id` = `user_has_conversations`.`user_id` AND `u`.`id` = ?", UserID).
        Find(&conversations)
    

    【讨论】:

    • 我正在使用 MySQL 并且在 SQL 查询的 Select 字段中出现错误。这对 MySQL 有用吗?
    • json_agg() 和 json_build_object() 是一个 postgres 函数。检查等效的 MySQL agg 函数,例如 JSON_ARRAYAGG() 和 JSON_OBJECTAGG()。
    猜你喜欢
    • 1970-01-01
    • 2017-04-23
    • 2021-08-31
    • 2021-05-20
    • 1970-01-01
    • 2022-07-07
    • 1970-01-01
    • 1970-01-01
    • 2021-10-02
    相关资源
    最近更新 更多