You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

go-jet查询结果仅能映射匿名结构体,命名结构体失效排查

go-jet查询结果无法映射到局部命名结构体问题

使用go-jet v2.11.1时遇到异常:查询结果可正确填充匿名结构体,但无法映射到函数内部定义的命名结构体,即使两者字段结构、标签完全一致。命名结构体仅值类型字段(如Email、Id)保留初始空值,指针类型字段无数据,而匿名结构体能完整获取所有查询结果。

代码示例

package api
import (
    "fmt"
    "reflect"

    "github.com/Napam/apato/internal/api/database"
    . "github.com/Napam/apato/internal/generated/apato/app/table"
    "github.com/Napam/apato/internal/utils/auth"
    "github.com/Napam/apato/internal/utils/logging"
    . "github.com/go-jet/jet/v2/postgres"
    "github.com/labstack/echo/v4"
    "github.com/rs/zerolog"
)

func (h *Handlers) GetAuthUser(c echo.Context) error {
    logger := logging.GetRequestLogger(c)
    logger.Info().Msg("Start get auth user")

    userId, err := auth.GetRequestUserId(c)
    if err != nil {
        return &echo.HTTPError{
            Code:    401,
            Message: "Unauthorized",
        }
    }
    logger.Info().Str("userId", userId).Msg("Retrieved user id from request context")

    selectStatment := Users.SELECT(
        Users.FirstName.AS("firstName"),
        Users.LastName.AS("lastName"),
        Users.Email.AS("email"),
        Users.ID.AS("id"),
        Users.ProviderName.AS("providerName"),
    ).WHERE(
        Users.ID.EQ(String(userId)),
    )

    logger.Debug().Func(func(e *zerolog.Event) {
        e.Str("SQL", selectStatment.DebugSql())
    }).Msg("SQL to get user")

    type TEST struct {
        Email        string  `json:"email"`
        FirstName    *string `json:"firstName,omitempty"`
        Id           string  `json:"id"`
        LastName     *string `json:"lastName,omitempty"`
        ProviderName *string `json:"providerName,omitempty"`
    }

    var DBUser = TEST{
        Email:        "",
        FirstName:    nil,
        Id:           "",
        LastName:     nil,
        ProviderName: nil,
    }

    var DBUser2 struct {
        Email        string  `json:"email"`
        FirstName    *string `json:"firstName,omitempty"`
        Id           string  `json:"id"`
        LastName     *string `json:"lastName,omitempty"`
        ProviderName *string `json:"providerName,omitempty"`
    }

    // Use reflection to inspect the fields of DBUser
    val := reflect.ValueOf(DBUser)
    for i := 0; i < val.NumField(); i++ {
        field := val.Type().Field(i)
        value := val.Field(i).Interface()
        logger.Debug().Str("Field", field.Name).Interface("Value", value).Interface("Tag", field.Tag).Msg("DBUser field value")
    }

    // Use reflection to inspect the fields of DBUser2
    val = reflect.ValueOf(DBUser2)
    for i := 0; i < val.NumField(); i++ {
        field := val.Type().Field(i)
        value := val.Field(i).Interface()
        logger.Debug().Str("Field", field.Name).Interface("Value", value).Interface("Tag", field.Tag).Msg("DBUser2 field value")
    }

    err = selectStatment.Query(database.DB, &DBUser)
    if err != nil {
        return fmt.Errorf("Error querying database: %w", err)
    }

    err = selectStatment.Query(database.DB, &DBUser2)
    if err != nil {
        return fmt.Errorf("Error querying database: %w", err)
    }

    logger.Debug().Interface("DBUser", DBUser).Msg("Retrieved user from database")
    logger.Debug().Interface("DBUser2", DBUser2).Msg("Retrieved user from database")

    return c.JSON(200, map[string]any{
        "DBUser":  DBUser,
        "DBUser2": DBUser2,
    })
}

实际输出

{
  DBUser: { email: '', id: '' },
  DBUser2: {
    email: 'xxxxx@xxxxxxx.com',
    firstName: 'Naphat',
    id: '123123123',
    lastName: 'Amundsen',
    providerName: 'google'
  }
}

环境信息

  • Golang: go1.23.1 darwin/arm64
  • 操作系统: MacOS Sonoma 14.6.1
  • 数据库: Postgres 16.4
  • 驱动: github.com/lib/pg v1.10.9
  • go-jet版本: v2.11.1

问题成因

核心原因是go-jet的反射映射机制无法正确处理函数内部定义的局部命名结构体。go-jet内部会缓存结构体的字段映射关系,而局部类型的包路径(PkgPath)为空,导致其无法被正确识别和缓存;此外,Go反射对局部类型的处理逻辑与包级类型存在差异,进一步导致字段映射失败。

而匿名结构体虽然也是局部定义,但go-jet对其采用了不同的反射处理逻辑,绕过了类型缓存的限制,因此能正常映射。

解决方法

  1. 将结构体移至包级定义:把命名结构体从函数内部移到包级别(即使不导出,只要在包级声明即可):
// 包级定义结构体
type TEST struct {
    Email        string  `json:"email"`
    FirstName    *string `json:"firstName,omitempty"`
    Id           string  `json:"id"`
    LastName     *string `json:"lastName,omitempty"`
    ProviderName *string `json:"providerName,omitempty"`
}

func (h *Handlers) GetAuthUser(c echo.Context) error {
    // ... 原有代码
    var DBUser TEST // 直接使用包级类型
    // ... 原有代码
}
  1. 使用jet标签明确映射关系:建议使用go-jet官方推荐的jet标签指定列名,避免自动转换的潜在问题:
type TEST struct {
    Email        string  `json:"email" jet:"email"`
    FirstName    *string `json:"firstName,omitempty" jet:"firstName"`
    Id           string  `json:"id" jet:"id"`
    LastName     *string `json:"lastName,omitempty" jet:"lastName"`
    ProviderName *string `json:"providerName,omitempty" jet:"providerName"`
}

内容的提问来源于stack exchange,提问作者Naphat Amundsen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 14:24:51