Golang中循环内如何扫描SQL行数据

Golang中循环内如何扫描SQL行数据 大家好 :wave:, 我是Go编程语言的新手,这是我在这个论坛的第一篇帖子,是一个在线课程推荐给我的。2022年还很新鲜,祝大家新年快乐。

我很难理解为什么不能在循环内返回一个sql行?或者我没有理解正确的做法?

我试图在PostgreSQL表中创建一个列,然后返回整个列的行并打印出来。我尝试在循环内完成这个操作(但行不通),而每当我在循环外做这件事时,它就能正常工作。请用你们的智慧启发我 :pray:。

数据库:

go-mvc=# select * from messages;
 id  | name  |     message     |         created_at
-----+-------+-----------------+----------------------------
 101 | Ori   | Skol!           | 2022-01-23 19:43:14.761144
 102 | Oin   | Raise the axes! | 2022-01-23 19:43:14.763818
 103 | Gloin | Bouncing around | 2022-01-23 19:43:14.764389
(3 rows)

控制器:

func CreateMessageProcessJSON(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		w.WriteHeader(405)
		return
	}
	if r.Header.Get("content-type") != "application/json" {
		w.WriteHeader(406)
		return
	}

	msgs := []models.MessageJSON{}
	helpers.ParseBody(r, &msgs)

	// 不工作
	// for _, msg := range msgs {
	// 	if err := msg.CreateFromJSON(); err != nil {
	// 		http.Error(w, http.StatusText(500), http.StatusInternalServerError)
	// 		return
	// 	}
	// }

	// 工作
	if err := msgs[0].CreateFromJSON(); err != nil {
		http.Error(w, http.StatusText(500), http.StatusInternalServerError)
		return
	}

	pd := models.PageDataJSON{
		msgs,
		models.RawData{Title: "Show json message"},
	}

	fmt.Fprintln(w, pd)
}

模型:

type MessageJSON struct {
	Id        int64     `json:"id"`
	Name      string    `json:"name"`
	Message   string    `json:"msg"`
	CreatedAt time.Time `json:"created_at"`
}

func (msg *MessageJSON) CreateFromJSON() (err error) {
	// 验证表单值
	if msg.Name == "" || msg.Message == "" {
		errors.New("this error just triggers another error")
		return
	}

	statement := `INSERT INTO messages (name, message, created_at) VALUES ($1, $2, $3) RETURNING id, name, message, created_at`

	stmt, err := db.Db.Prepare(statement)
	if err != nil {
		return
	}

	// 使用 QueryRow 返回一行并将返回的 id 扫描到 User 结构体中
	err = stmt.QueryRow(msg.Name, msg.Message, time.Now()).Scan(&msg.Id, &msg.Name, &msg.Message, &msg.CreatedAt)
	return
}

每次我发出POST请求时,列都会被创建,但 scan 似乎对我的结构体没有任何影响。

带循环的POST请求:

% curl -X POST \
http://localhost:8080/messages/createjson/process \
-H "Content-Type: application/json" \
-d '[{"name":"Ori", "msg":"Skol!"},{"name":"Oin","msg":"Raise the axes!"},{"name":"Gloin","msg":"Bouncing around"}]' -v

Note: Unnecessary use of -X or --request, POST is already inferred.
*   Trying ::1:8080...
* connect to ::1 port 8080 failed: Connection refused
*   Trying 127.0.0.1:8080...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> POST /messages/createjson/process HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.77.0
> Accept: */*
> Content-Type: application/json
> Content-Length: 111
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Sun, 23 Jan 2022 18:43:14 GMT
< Content-Length: 178
< Content-Type: text/plain; charset=utf-8
<
{[{0 Ori Skol! 0001-01-01 00:00:00 +0000 UTC} {0 Oin Raise the axes! 0001-01-01 00:00:00 +0000 UTC} {0 Gloin Bouncing around 0001-01-01 00:00:00 +0000 UTC}] {Show json message}}
* Connection #0 to host localhost left intact

不带循环的POST请求(会给我正确的ID,但这不是我想要的):

% curl -X POST \
http://localhost:8080/messages/createjson/process \
-H "Content-Type: application/json" \
-d '[{"name":"ID?", "msg":"CHEERS!!!"}]' -v
Note: Unnecessary use of -X or --request, POST is already inferred.
*   Trying ::1:8080...
* connect to ::1 port 8080 failed: Connection refused
*   Trying 127.0.0.1:8080...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> POST /messages/createjson/process HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.77.0
> Accept: */*
> Content-Type: application/json
> Content-Length: 35
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Sun, 23 Jan 2022 18:52:18 GMT
< Content-Length: 83
< Content-Type: text/plain; charset=utf-8
<
{[{105 ID? CHEERS!!! 2022-01-23 19:52:18.941026 +0000 +0000}] {Show json message}}
* Connection #0 to host localhost left intact

保重。


更多关于Golang中循环内如何扫描SQL行数据的实战教程也可以访问 https://www.itying.com/category-94-b0.html

4 回复

感谢您的反馈,很高兴能帮上忙。

更多关于Golang中循环内如何扫描SQL行数据的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


非常感谢 @christophberger,这不仅解决了我的问题,还让我豁然开朗! 🎉

你好 @lmllr,

这是一个经典的 Go 语言陷阱,如果你不习惯的话很容易忽略。

在这个循环表达式中,msg 是相应数组元素的副本

lmllr:

for _, msg := range msgs {
    if err := msg.CreateFromJSON();

要将 CreateFromJSON() 的结果存入数组,请尝试这样做:

for i := range msgs {
    if err := msgs[i].CreateFromJSON();

问题在于你的 CreateFromJSON() 方法使用了指针接收器,但在循环中传递的是切片的副本。当你在循环中迭代 msgs 时,每个 msg 都是切片元素的副本,而不是原始元素的引用。

以下是修复后的代码:

func CreateMessageProcessJSON(w http.ResponseWriter, r *http.Request) {
    if r.Method != http.MethodPost {
        w.WriteHeader(405)
        return
    }
    if r.Header.Get("content-type") != "application/json" {
        w.WriteHeader(406)
        return
    }

    msgs := []models.MessageJSON{}
    helpers.ParseBody(r, &msgs)

    // 修复:使用索引访问原始切片元素
    for i := range msgs {
        if err := msgs[i].CreateFromJSON(); err != nil {
            http.Error(w, http.StatusText(500), http.StatusInternalServerError)
            return
        }
    }

    pd := models.PageDataJSON{
        msgs,
        models.RawData{Title: "Show json message"},
    }

    fmt.Fprintln(w, pd)
}

或者,如果你需要修改结构体字段,可以使用指针切片:

func CreateMessageProcessJSON(w http.ResponseWriter, r *http.Request) {
    if r.Method != http.MethodPost {
        w.WriteHeader(405)
        return
    }
    if r.Header.Get("content-type") != "application/json" {
        w.WriteHeader(406)
        return
    }

    msgs := []*models.MessageJSON{}
    helpers.ParseBody(r, &msgs)

    // 现在可以使用范围循环,因为每个元素都是指针
    for _, msg := range msgs {
        if err := msg.CreateFromJSON(); err != nil {
            http.Error(w, http.StatusText(500), http.StatusInternalServerError)
            return
        }
    }

    pd := models.PageDataJSON{
        msgs,
        models.RawData{Title: "Show json message"},
    }

    fmt.Fprintln(w, pd)
}

同时需要更新 PageDataJSON 结构体以支持指针切片:

type PageDataJSON struct {
    Messages []*MessageJSON `json:"messages"`
    RawData  RawData        `json:"raw_data"`
}

这样修改后,Scan() 方法就能正确更新结构体字段了。

回到顶部