我正处于学习Go的早期阶段,我正试图熟悉Go与SQL数据库交互的方式。我曾广泛使用SQL数据库,但几乎从未使用过ORM。
作为从数据库中插入/删除的第一个简单练习,我编写了函数addCardTest()来向MySQL数据库中的表添加一行,并编写了函数listCardsTest()来打印对该表的简单查询的结果。在main()函数中,我尝试按此顺序运行这两个函数。但是,我在运行第二个函数时收到一个错误:
panic: dial tcp: lookup rpi.local on 172.24.32.1:53: no such host
goroutine 1 [running]:
main.listCardsTest()
/home/tmiku/go/src/mtg/mtg.go:23 +0x325
main.main()
/home/tmiku/go/src/mtg/mtg.go:12 +0x1c
exit status 2
我发现,如果我从调用的第一个函数中删除“deferdb.Close()”行,这个错误就会消失。我很惊讶这起作用了,这让我有很多问题,首先是什么抛出了这个错误。
- 在引擎盖下,是否有一些并发工作正在发生(即listCardsTest()在addCardsTest()完成之前运行)?在Go中使用外部数据库时,这是预期的吗?
- 一个Go程序只能创建一个连接池到任何给定的数据库吗?当我尝试打开另一个时会发生什么?这是一个有意义的限制,但“没有这样的主机”错误似乎是一个奇怪/不直观的方式来强制执行。
- 访问数据库的函数是否应该在假设连接已经打开的情况下编写,并接受数据库信息作为参数?将数据库连接的打开/关闭 Package 到一个执行其他操作的函数中是不是不好?感觉就像是这个错误把我推向了这个方向,但我很难找到任何明显的建议记录。
完整代码在下面。MySQL数据库的主机名是rpi.local,请原谅硬编码的凭据(我正在学习)。
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
func main() {
addCardTest()
listCardsTest()
}
func listCardsTest() {
db, err := sql.Open("mysql", "tmiku:pass@tcp(rpi.local:3306)/mtg")
if err != nil {
panic(err)
}
defer db.Close()
rows, err := db.Query("select cardid, name, manacost, type from Cards")
if err != nil {
panic(err)
}
defer rows.Close()
var id int
var name, manacost, cardtype string // need to initialize these first...
for rows.Next() {
err := rows.Scan(&id, &name, &manacost, &cardtype) // ...so Scan() has somewhere to put them.
if err != nil {
panic(err)
}
//print the results
fmt.Println(id, name, manacost, cardtype)
}
}
func addCardTest() {
//open the database, db is a pointer to the db object
db, err := sql.Open("mysql", "tmiku:pass@tcp(rpi.local:3306)/mtg")
if err != nil {
panic(err)
}
defer db.Close() //REMOVED THIS LINE, FIXED ERROR
//run our insert
result, err := db.Exec("insert into mtg.Cards (name, manacost, ruletext, type, subtype, power, toughness) values ('Stormchaser Drake', '1U', 'Flying\nWhenever Stormchaser Drake becomes the target of a spell you control, draw a card.', 'Creature', 'Drake', 2, 1)")
if err != nil {
panic(err)
}
lastId, _ := result.LastInsertId()
fmt.Println("Added id ", lastId)
}
1条答案
按热度按时间35g0bw711#
我已经对您的代码进行了一点重构,以便您更好地理解我通常是如何编写这段代码的。首先,让我展示一下代码,然后,我将带您浏览所有相关部分。出于演示的目的,所有代码都被写入了一个名为
main.go
的文件中:现在,让我们仔细看看每个部分。
请注意,为了通过Docker运行MySQL示例,我使用了以下命令:
docker run --name some-mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root -d mysql
。addCardTest
函数在这里,有几个改进:
Exec
方法来执行命令(例如:INSERT
语句)而不是Query
现在,让我们切换到阅读逻辑。
listCardsTest
函数在这里,代码或多或少与以前相同。
main
函数在这个函数中,您应该示例化一个从现在开始必须使用的MySQL DB的句柄。您只需要调用函数
sql.Open()
一次,defer
对方法Close()
进行一次调用。所有这些都必须在main
函数中完成。然后,您应该在与DB相关的函数中期望这种类型的参数(例如上面提到的listCardsTest
和addCardTest
)。我强烈建议你查看这个链接,它解释了
sql.DB
结构体应该如何使用。让我知道如果我的程序可以帮助你解决这个问题,谢谢!