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

Go API调用Sql.Query()无返回数据问题求助

问题根源与修复方案

你的代码核心问题是直接将*sql.Rows对象返回并序列化JSON——这完全行不通,*sql.Rows是Go标准库database/sql的底层结果集迭代器,它只是持有查询结果的游标,并没有真正读取数据,也不支持直接JSON序列化。另外你也没有正确关闭结果集,会导致数据库连接泄漏。

修复步骤

1. 完善数据库初始化(添加连接有效性验证)

sql.Open只是创建连接池,不会立即建立连接,所以在初始化时加上Ping验证:

func InitializeDatabase() error {
    db, err := sql.Open("mysql", dsn(os.Getenv("db_dbname")))
    if err != nil {
        return err
    }
    // 验证连接是否真的可用
    if err := db.Ping(); err != nil {
        db.Close()
        return err
    }
    api_db = db
    return nil
}

2. 重写ExecuteCommand,正确读取查询结果

不要直接返回*sql.Rows,而是把结果集读取到可序列化的数据结构中,同时必须关闭结果集防止连接泄漏:

方案一:用结构体映射(类型安全,推荐)

先定义与users表字段匹配的结构体:

// 根据你的实际表字段调整字段名和类型
type User struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email"`
    // 其他字段...
}

然后修改ExecuteCommand:

func ExecuteCommand(command string) any {
    log.Printf("Executing %v\n", command)

    rows, err := api_db.Query(command)
    if err != nil {
        log.Printf("Error %v when executing DB\n", err.Error())
        return nil
    }
    defer rows.Close() // 确保结果集被关闭

    var users []User
    for rows.Next() {
        var u User
        // 注意字段顺序要和SELECT返回的列顺序一致
        err := rows.Scan(&u.ID, &u.Name, &u.Email)
        if err != nil {
            log.Printf("Failed to scan row: %v", err)
            continue
        }
        users = append(users, u)
    }

    // 检查遍历过程中是否出现错误
    if err := rows.Err(); err != nil {
        log.Printf("Row iteration error: %v", err)
        return nil
    }

    return users
}

方案二:用动态Map(适合不确定表结构的场景)

如果不想定义结构体,可以用map[string]any存储每行数据:

func ExecuteCommand(command string) any {
    log.Printf("Executing %v\n", command)

    rows, err := api_db.Query(command)
    if err != nil {
        log.Printf("Error %v when executing DB\n", err.Error())
        return nil
    }
    defer rows.Close()

    cols, err := rows.Columns()
    if err != nil {
        log.Printf("Failed to get columns: %v", err)
        return nil
    }

    // 准备用于Scan的指针切片
    values := make([]any, len(cols))
    valuePtrs := make([]any, len(cols))
    for i := range values {
        valuePtrs[i] = &values[i]
    }

    var results []map[string]any
    for rows.Next() {
        err := rows.Scan(valuePtrs...)
        if err != nil {
            log.Printf("Failed to scan row: %v", err)
            continue
        }

        row := make(map[string]any)
        for i, col := range cols {
            // 处理NULL值(可选)
            if values[i] == nil {
                row[col] = nil
            } else {
                row[col] = values[i]
            }
        }
        results = append(results, row)
    }

    if err := rows.Err(); err != nil {
        log.Printf("Row iteration error: %v", err)
        return nil
    }

    return results
}

3. 修正控制器的状态码

http.StatusAccepted表示请求已接受但未处理完成,查询成功应该用http.StatusOK:

func MainController(w http.ResponseWriter, r *http.Request) {
    res := database.ExecuteCommand("SELECT * FROM users;")
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(http.StatusOK)
    json.NewEncoder(w).Encode(res)
}

额外注意事项

  • 禁止直接拼接SQL语句,会引发SQL注入风险,必须使用参数化查询:api_db.Query("SELECT * FROM users WHERE id = ?", userID)
  • 可以给连接池配置合理的参数(如SetMaxOpenConns、SetMaxIdleConns),避免连接泄漏或资源耗尽

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 09:33:34