在 Golang 中执行 SQL 查询

5

我看到人们使用Golang内置的database/sql查询有两种方式。其中一种是使用fmt.Sprintf

func (db *DB) CreateUserTable() (sql.Result, error) {
    statement := "CREATE TABLE %s (%s, %s, %s, %s, %s)"
    v := []interface{}{"User", "ID int PRIMARY KEY NOT NULL", "Name varchar(100) UNIQUE", "Email varchar(100) UNIQUE", "Address varchar(100) ",  "Username varchar(100) UNIQUE"}
    return db.Exec(fmt.Sprintf(statement, v...))
}

另一种方法是使用预处理语句(prepared statement):

func (db *DB) CreateUserTable() (sql.Result, error) {    
    statement, err := db.Prepare("INSERT INTO User(tbl1,tbl2,tbl3) VALUES(?,?,?)")
    if err != nil {
        log.Fatal(err)
    }
    return statement.Exec("value1", "value2", "value3")
}

第一个可以通过动态设置表名、列名和值来获得好处,但第二个只针对值。两者有何区别?我应该使用哪个?
2个回答

17

永远不要从来自系统外部的字符串构建SQL。

enter image description here

始终使用?语法。

如果必须设置SQL部分,例如表名,请准备多个完整的SQL语句,其中包含值的?。选择要执行的SQL,可能基于用户输入,但永远不要从用户输入中构建SQL。


3

使用预处理语句更加清晰,这样每当需求改变时,您可以轻松修改语句。此外,还可以防止SQL注入。

与连接字符串相比,预处理语句更好,原因有很多(例如避免SQL注入攻击)。

在MySQL中,参数占位符为“?”;在PostgreSQL中,它是$N,其中N是数字。SQLite接受这两种方式。

另外一件事是,预处理语句可用于重复的方法,可以执行多次并且可以被销毁。

stmt, err := db.Prepare("select id, name from users where id = ?")
if err != nil {
    log.Fatal(err)
}
defer stmt.Close() // closing the statement
rows, err := stmt.Query(1)

而且你正在使用接口

func (db *DB) CreateUserTable() (sql.Result, error) {
    statement := "CREATE TABLE %s (%s, %s, %s, %s, %s)"
    v := []interface{}{"User", "ID int PRIMARY KEY NOT NULL", "Name varchar(100) UNIQUE", "Email varchar(100) UNIQUE", "Address varchar(100) ",  "Username varchar(100) UNIQUE"}
    return db.Exec(fmt.Sprintf(statement, v...))
}

在幕后,此函数可以接受任何类型的参数,这可能会存在漏洞。
要获取更详细的信息,请访问此链接

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接