Golang中MySQL语句问题如何解决

Golang中MySQL语句问题如何解决 我有以下代码:

if queryuser, err = db.Prepare("select UserID, CurrentDB from optUsers where LoginHash = '?'"); err != nil  {
  println(err)
  panic( "queryuser Statement failed to create" )
}   

var1 := req.URL.Query().Get("hash")
row := queryuser.QueryRow( var1 )
var userinfo user
row.Scan(&userinfo.UserID, &userinfo.CurrentDB)

问题是如果我使用预处理语句执行查询,就得不到任何结果。但如果我使用 db.QueryRow() 并直接内联执行查询,就能得到结果。有人看出我的预处理语句代码有什么问题吗?

谢谢。


更多关于Golang中MySQL语句问题如何解决的实战教程也可以访问 https://www.itying.com/category-94-b0.html

21 回复

row.Scan 同样会返回一个错误。捕获该错误并检查其内容。

更多关于Golang中MySQL语句问题如何解决的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


啊,好的。是的,这样修复了。非常感谢。

格伦

格伦, 非常感谢!我计划今晚重构代码,将数据库设为全局变量。感谢您的反馈。

尝试写 ? 而不是 ‘?’。我认为撇号会让它被识别为普通字符串。

你的数据库已经是一个全局变量。因此只需在主函数块中创建它,而不是在每次调用时都创建。

我可能理解有误,但查询中的 '?' 难道不是表示包含字面问号的字符串吗?我会省略引号。

NobbZ,你在哪里看到我执行了exec操作?我正在使用queryrow但没有得到返回结果。但如果我切换到普通的db.QueryRow而不使用预处理语句,它就能正常工作。

我不确定我是否理解你的问题是什么?

如果你所说的"执行"语句是指对语句调用 Exec(),那么是的,你不会得到任何数据库项返回,因为 Exec() 是用于写操作的。

如果你想读取数据,那么你应该使用 QueryRow() 并在结果集上迭代直到它为空。

我还假设它会自动为字符串等内容添加引号?因为移除引号后错误消失了,但我为该参数传入的字符串确实需要引号才能构成正确的查询。

另外我似乎没有从 QueryRow 本身收到错误响应,所以不太确定它是否正在正确执行。

Glenn_Hancock:

NobbZ, where do you see me doing an exec

我没有看到,但你说你想“执行”查询,我问你指的是不是 Exec 方法,现在看来你并不是这个意思。

你看到任何错误信息了吗?你检查过函数调用返回的 error 值了吗?

首先,我对Scan函数返回错误却没有强制我捕获它感到惊讶。我以为只要不想使用返回变量就必须用下划线处理。所以我完全不知道这个函数还有返回值。

其次,我收到的错误信息显示"sql: expected 0 arguments, got 1"。但在我的语句中明明创建了一个参数,所以我还是不确定问题出在哪里。是我传递变量的方式不对还是其他原因?

谢谢,

好的,现在那个错误消失了,但我遇到了"结果集中没有行"的问题。不过,如果我使用 db.QueryRow 来运行这个查询,它又能完美工作。所以问题在于这个语句认为没有找到任何内容。

另外,我是否需要在这里处理 SQL 注入的问题?我不确定是否默认会对内容进行转义,或者是否需要采取其他措施来确保没有人能干扰我的查询。

谢谢。

Glenn_Hancock:

好的,这样那个错误就消失了,但现在我得到"结果集中没有行"。但再次说明,如果我使用 db.QueryRow 运行这个,它工作得很好。所以问题在于语句认为没有找到任何内容。

能否请你提供两种方法的示例?

同时告诉我们你的表结构,即使有示例数据也会有很大帮助。

当然,你需要简化示例表,避免复杂的查询甚至连接。

我遇到了多个问题,目前仍在研究最后一个。最初我使用的是默认的 HTTP 处理方式,最近改用 Gorilla Mux 进行尝试。在这个过程中,我将我的变量改为使用 vars := mux.Vars(req),据说它会把变量放入映射中,但显然访问 vars["hash"] 并不是正确的方式。我硬编码了哈希值后就能正常工作了。所以与这个主题相关的问题通过移除问号周围的引号得到了解决。

但是,我能否确信这种方法能安全防范 SQL 注入?我知道我之前的方法并不安全,这就是为什么我在研究预处理语句的原因。

感谢您的帮助。

Glenn_Hancock:

query := "select UserID, CurrentDB from optUsers where LoginHash='" + var1 + "'"

你不应该这样做!这容易受到SQL注入攻击。

你检查过var1的实际值吗?可能它并不完全符合你的预期。你试过将其硬编码为一个确定存在于数据库中的值吗?你尝试过预编译语句的简化版本吗?db.Query("select UserID, CurrentDB from optUsers where LoginHash = ?", var1)

我目前无法自己设置测试程序,因为这台计算机的资源相当有限,安装更多软件可能会使磁盘空间不足wink

你好 Glen!我在使用 MySQL 和 db.prepare 语句时也遇到了一些问题。你可以参考我的代码看看我是如何解决的(我对 Go 和开发还比较陌生,所以代码还需要进一步整理。不过你可以看到我是如何执行查询的)

GitHub

djohn002/gogoalsapp

头像

这是我的第一个 Go 应用程序,使用本地 MySQL 数据库,Go 作为服务器,CSS/Bootstrap 作为前端。这个应用是我在没有参考任何教程的情况下创建的,通过拼凑我能找到的资料完成……

Dennis,

你没有使用语句,而是采用了我之前提到的对我同样有效的方法——直接运行查询。不过我想指出几点你可能需要考虑的事项。

  1. 你在每个函数内部创建数据库连接。根据我所了解的情况,实际上只需要在 main 函数中创建一个数据库连接,然后将其作为全局变量在其他函数中使用。这将显著提升运行速度。

  2. 我认为你可能过晚地使用了 defer。使用 defer 语句的目的是确保无论发生什么,某些操作都会执行。在你的例子中是关闭数据库连接,但如果你的代码在到达函数底部关闭连接之前就出错了会怎样?因此,你应该做的是在创建连接后立即使用 defer 来关闭它。这样,即使在代码执行到底部之前发生问题,它仍然会为你关闭连接。按照你现在的用法,你大可以直接用 close 替换 defer,然后移除 defer。

mux.Vars() 返回的是路由中的变量,而不是查询字符串中的变量。

要获取查询字符串及其值,你仍然需要按照之前的方式处理,至少从快速浏览 mux 文档来看是这样。

预处理语句的安全性取决于你所使用的关系型数据库管理系统中的预处理语句安全性。

Go 语言实际上是这样与你的关系型数据库通信的:

Go:嘿,在不久的将来,有人可能会询问你这个查询:“select UserID, CurrentDB from optUsers where LoginHash = ?”
关系型数据库:好的,我会把这个查询记为"1"
Go:嘿,你还记得查询1吗?这是缺失的参数:"deadb33f",它是一个字符串。
关系型数据库:好的,这是你的结果。…

从语言/数据库驱动程序的角度来看,你无法再采取更多措施来增强或减弱对 SQL 注入的防护。这与你是否在 PHP、Go、Rust 或其他任何语言中使用预处理语句无关。

表结构较长,但这里只关注两个字段: UserID int unsigned not null auto_increment CurrentDB char(50) LoginHash char(50)

userinfo 是一个包含两个字段的结构体:

type user struct {
UserID int              `json:"userid"`
CurrentDB NullString    `json:"currentdb"`
}

可正常工作的代码:

var1 := req.URL.Query().Get("hash")
query := "select UserID, CurrentDB from optUsers where LoginHash='" + var1 + "'"
row := db.QueryRow( query );
row.Scan(&userinfo.UserID, &userinfo.CurrentDB)

无法正常工作的代码:

queryuser, err = db.Prepare("select UserID, CurrentDB from optUsers where LoginHash = ?")
var1 := req.URL.Query().Get("hash")
row := queryuser.QueryRow( var1 )
row.Scan(&userinfo.UserID, &userinfo.CurrentDB)

顺便说明:这个调用没有实际用途,只是尝试用Go编写一个简单测试来理解整个流程。我考虑将来用Go重写一个大型PHP应用程序,目前正通过这个程序来完善细节。

问题出在你的预处理语句中单引号的使用上。在预处理语句中,占位符 ? 不应该被单引号包围,即使对应的值是字符串类型。

错误代码:

db.Prepare("select UserID, CurrentDB from optUsers where LoginHash = '?'")

正确代码:

if queryuser, err = db.Prepare("select UserID, CurrentDB from optUsers where LoginHash = ?"); err != nil  {
  println(err)
  panic( "queryuser Statement failed to create" )
}

当使用预处理语句时,数据库驱动会自动处理参数的类型和转义。将占位符 ? 放在单引号内会导致它被当作字面字符串值 "?" 而不是参数占位符。

以下是完整的修正示例:

// 正确的预处理语句
if queryuser, err = db.Prepare("select UserID, CurrentDB from optUsers where LoginHash = ?"); err != nil {
    println(err)
    panic("queryuser Statement failed to create")
}

var1 := req.URL.Query().Get("hash")
row := queryuser.QueryRow(var1)
var userinfo user
if err := row.Scan(&userinfo.UserID, &userinfo.CurrentDB); err != nil {
    if err == sql.ErrNoRows {
        println("No user found with the provided hash")
    } else {
        println("Scan error:", err)
    }
}

预处理语句的优势在于:

  • 防止SQL注入攻击
  • 提高重复查询的性能
  • 自动处理参数类型转换和转义
回到顶部