如何在Go中解锁数据库

huangapple go评论88阅读模式
英文:

How can i unlock the Database in Go

问题

我是你的中文翻译助手,以下是翻译好的内容:

我是一个Go语言的新手,对SQL也不是很擅长。

我在数据库中有一个名为users的简单表格。我在表格中存储了SAM、名字和姓氏。当我尝试在数据库中进行更改时,我收到了"database is locked"的错误。这是我的代码:

func createNewUser(w http.ResponseWriter, r *http.Request) {
	var user User
	err := decodeJSONBody(w, r, &user)
	if checkError(w, err) {
		return
	}
	rows, err := mainDB.Query("SELECT * FROM users WHERE SAM = ?", user.Sam)
	if checkError(w, err) {
		return
	}
	defer rows.Close()
	if rows.Next() {
		http.Error(w, "User already exists", http.StatusConflict)
		return
	}
	_, err = mainDB.Exec("INSERT INTO users (SAM, Vorname, Nachname) VALUES (?, ?, ?)", user.Sam, user.Vorname, user.Nachname)
	if checkError(w, err) {
		return
	}
	json.NewEncoder(w).Encode(user)
}

decodeJSONBodycheckError函数是正常工作的,与数据库无关。
据我所了解,rows.Close应该关闭列,以便我可以写入一些内容。

英文:

Im a newbie in go and not the best in sql.

I have a simple Table in my Database with the name of users. I store the SAM, First Name and Last Name in the table. When i now try to change something in the database, i get the error database is locked. Thats my code:

func createNewUser(w http.ResponseWriter, r *http.Request) {
	var user User
	err := decodeJSONBody(w, r, &user)
	if checkError(w, err) {
		return
	}
	rows, err := mainDB.Query("SELECT * FROM users WHERE SAM = ?", user.Sam)
	if checkError(w, err) {
		return
	}
	defer rows.Close()
	if rows.Next() {
		http.Error(w, "User already exists", http.StatusConflict)
		return
	}
	_, err = mainDB.Exec("INSERT INTO users (SAM, Vorname, Nachname) VALUES (?, ?, ?)", user.Sam, user.Vorname, user.Nachname)
	if checkError(w, err) {
		return
	}
	json.NewEncoder(w).Encode(user)
}

decodeJSONBody and checkError work and have nothing to do with the database.
And as far as I've learned, rows.Close should close the columns so that I can write something back in

答案1

得分: 0

根据评论,SQLite在锁定/并发方面有一些限制,这意味着在同时运行多个语句时需要小心。不幸的是,当我发表评论时,我没有详细审查你的代码,所以尽管似乎解决了问题,但是这是错误的。

你添加了defer rows.Close();这将释放用于运行查询的数据库连接,但是由于defer的存在,这只会在包围函数返回时发生。通常这不是一个大问题,因为完整地遍历结果集会自动关闭rows文档中指出:

>如果调用Next并返回false,并且没有进一步的结果集,则Rows会自动关闭,只需检查Err的结果即可。

在你的代码中,如果rows.Next()为true,则会返回:

if rows.Next() {
    http.Error(w, "User already exists", http.StatusConflict)
    return
}

这意味着不需要添加额外的rows.Close()。然而,由于你说“添加了多次rows.Close(),现在它可以工作”,我怀疑你的完整代码可能比所呈现的更复杂(并且其中一个添加的rows.Close()是必需的)。

因此,不需要添加额外的rows.Close()调用;它不会引起问题(除了不必要的函数调用)。然而,你应该检查错误:

rows, err := mainDB.Query("SELECT * FROM users WHERE SAM = ?", user.Sam)
if checkError(w, err) {
    rows.Close()
    return
}
if rows.Next() {
    http.Error(w, "User already exists", http.StatusConflict)
    return
}
if err = rows.Err(); err != nil {
   return // 在这里检查错误是值得的
}

请注意,go-sqlite3的常见问题解答中包含处理“Error: database is locked”错误的信息(值得确保你遵循建议)。

注意2:考虑使用EXISTS而不是运行查询,然后尝试获取一行数据-这可能更快,并且允许你使用QueryRow,从而简化你的代码。

英文:

As per the comments SQLite has some limitations around locking/concurrency which means you need to take care when running multiple statements concurrently. Unfortunately I had not reviewed your code in detail when posting my comment so, despite seemingly solving the issue, it was in error.

You had added a defer rows.Close(); this will free up the database connection used to run the query but, due to the defer, this will only happen when the surrounding function returns. Normally this is not a big issue because looping through a result set in its entirety automatically closes the rows. The documentation states:

>If Next is called and returns false and there are no further result sets, the Rows are closed automatically and it will suffice to check the result of Err.

In your code you do return if rows.Next() is true:

if rows.Next() {
    http.Error(w, "User already exists", http.StatusConflict)
    return
}

This means that adding an extra rows.Close() should not be needed. However as you say "added rows.Close() multiple times, and now it works" I suspect that your full code may have been a bit more complicated than that presented (and one of the added rows.Close() was needed).

So adding extra calls to rows.Close() should not be needed; it will not cause an issue (other than an unnecessary function call). However you should check for errors:

rows, err := mainDB.Query("SELECT * FROM users WHERE SAM = ?", user.Sam)
if checkError(w, err) {
    rows.Close()
    return
}
if rows.Next() {
    http.Error(w, "User already exists", http.StatusConflict)
    return
}
if err = rows.Err(); err != nil {
   return // It's worth checking fort an error here
}

Note that the FAQ for go-sqlite3 includes information on dealing with "Error: database is locked" (and it's worth ensuring you follow the recommendations).

Note2: Consider using EXISTS instead of running the query and then attempting to fetch a row - it is likely to be faster and allows you to use QueryRow which simplifies your code.

huangapple
  • 本文由 发表于 2021年11月23日 19:44:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/70080315.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定