Golang向MySQL表插入重复记录的问题解决

Golang向MySQL表插入重复记录的问题解决 我已经成功使用GO查询我的MySQL表。但现在在向MySQL表插入记录时遇到了问题。我写了一个小的GO测试程序,列出了数据库表的内容,运行编译后的测试程序,然后再次列出MySQL表的内容。

令我惊讶的是,记录被插入了两次!!

以下是我的测试程序的源代码:

package main

import (
	_ "github.com/go-sql-driver/mysql"
	"database/sql"
	"log"
	"fmt"
	"os"
)

func main(){
	mysql_connect_string := "myusername:mypassword@(127.0.0.1:3306)/mydatabase?parseTime=true"

	db, err := sql.Open("mysql", mysql_connect_string)
	if err != nil {
		log.Fatal(err)
	}

	tx,_:= db.Begin()
	stmt, err := tx.Prepare("INSERT INTO authors(name) VALUES(?)")
	res,err := stmt.Exec("Larry Wall")
	if err != nil {
		tx.Rollback()
		log.Fatalf("\n1st insert failed\n%q\n",err)
	}

	res,err = stmt.Exec("Randall Schwarz")
	if err != nil {
		tx.Rollback()
		log.Fatalf("\n2nd insert failed\n%q\n",err)
	}
	tx.Commit()
	fmt.Printf("\nresult :\n%q\n",res)
	os.Exit(0)
	
} // main

每次插入操作都产生了2条相同的记录。为什么会出现这些重复的插入?


更多关于Golang向MySQL表插入重复记录的问题解决的实战教程也可以访问 https://www.itying.com/category-94-b0.html

8 回复

你需要检查作者表中哪个字段是主键(或者也许这个表甚至没有主键,这样你就可以将姓名设为主键)。

更多关于Golang向MySQL表插入重复记录的问题解决的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


这可能是因为你使用的字段没有为关系型数据库管理系统提供判断其是否重复的线索。 这样做:清空表,然后逐步执行你的应用程序或进行调试…

我已删除表中的所有记录,现在无论我如何运行测试,都不会再出现重复记录。现在,我可能需要重构那个最初让我发现此问题的、规模更大的程序和数据库表。

你的解决方案除了一个问题外都说得通:在我执行插入操作之前,作者“larry wall”并不存在。为什么INSERT操作会导致创建两条记录?

从你的描述中我了解到,第二次运行测试程序时你只找到了几条数据。我认为这是因为你在运行测试前没有清除历史测试数据。这给你造成的错觉是运行测试后数据变多了,实际上这是多次运行测试的结果。

在这种情况下,你可以插入多个同名的作者,因此首先需要查询该作者是否存在,如果不存在,再添加他/她。代码可能如下所示:

sqlString := "SELECT name FROM authors WHERE name= ?"
err := db.QueryRow(sqlString, name).Scan(&name)
if err != nil && err == sql.ErrNoRows {
   // 未找到,直接插入
} else {
	// 作者已存在
}

该表的主键是一个 INTEGER AUTO_INCREMENT 字段。

mysql> desc authors;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| Id    | int(11)     | NO   | PRI | NULL    | auto_increment |
| Name  | varchar(25) | YES  |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
2 rows in set (0.08 sec)

mysql>

问题出在事务处理上。当第一次插入失败时,你调用了tx.Rollback(),但程序没有退出,继续执行了第二次插入。由于事务已经回滚,第二次插入会重新执行,导致重复记录。

以下是修正后的代码示例:

package main

import (
	_ "github.com/go-sql-driver/mysql"
	"database/sql"
	"log"
	"fmt"
)

func main() {
	mysql_connect_string := "myusername:mypassword@(127.0.0.1:3306)/mydatabase?parseTime=true"

	db, err := sql.Open("mysql", mysql_connect_string)
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	tx, err := db.Begin()
	if err != nil {
		log.Fatal(err)
	}

	stmt, err := tx.Prepare("INSERT INTO authors(name) VALUES(?)")
	if err != nil {
		tx.Rollback()
		log.Fatal(err)
	}
	defer stmt.Close()

	_, err = stmt.Exec("Larry Wall")
	if err != nil {
		tx.Rollback()
		log.Fatal(err)
	}

	_, err = stmt.Exec("Randall Schwarz")
	if err != nil {
		tx.Rollback()
		log.Fatal(err)
	}

	err = tx.Commit()
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("Insert completed successfully")
}

关键修改:

  1. 使用log.Fatal()替代log.Fatalf(),确保插入失败时程序立即退出
  2. 添加了所有错误检查,包括db.Begin()tx.Prepare()tx.Commit()
  3. 添加了defer语句确保资源正确释放
  4. 移除了不必要的res变量,因为插入操作不需要结果

这样确保任何一步失败都会立即终止程序,避免重复执行插入操作。

回到顶部