笔记内容按照 中文文案排版指北 进行排版 (https://github.com/sparanoid/chinese-copywriting-guidelines),以保证内容的可读性。
sqlx 库 NamedExec 插入问题
最近项目需要操作 mysql 数据库,本着学了一段时间的 go 语言,打算利用 go 语言来试试,通过学习教程,发现目前用 sqlx 库比较常见,那就试试呗。。
增删改查肯定不能少,首先从添加开始,那肯定要试试多条记录插入啦,看了一下 sqlx 库的文档,发现利用 NamedExec 可以实现。
// batch insert with structs
personStructs := []Person{
{FirstName: "Ardie", LastName: "Savea", Email: "asavea@ab.co.nz"},
{FirstName: "Sonny Bill", LastName: "Williams", Email: "sbw@ab.co.nz"},
{FirstName: "Ngani", LastName: "Laumape", Email: "nlaumape@ab.co.nz"},
}
_, err = db.NamedExec(`INSERT INTO person (first_name, last_name, email)
VALUES (:first_name, :last_name, :email)`, personStructs)
本以为很简单,不曾想刚开始就结束了,直接报错了。。
sql 如下
create database if not exists test;
use test;
DROP TABLE IF EXISTS `test`;
create table test(
id int not null,
type int not null,
commitId int,
cmdArgv varchar(500),
addition varchar(50)
)engine innodb default charset utf8;
demo
package main
import (
"fmt"
_ "github.com/go-sql-driver/mysql"
"github.com/jmoiron/sqlx"
)
type Test struct {
Id int `db:"id"`
Types int `db:"type"`
CommitId int `db:"commitId"`
CmdArgv string `db:"cmdArgv"`
Addition string `db:"addition"`
}
var (
db *sqlx.DB
)
func init() {
//test 是数据库名
database, err := sqlx.Connect("mysql", "user:passwd@tcp(127.0.0.1:3306)/test")
if err != nil {
fmt.Println("连接数据库失败:", err)
return
}
db = database
fmt.Println("连接数据库成功!")
}
func Finalizer() {
db.Close()
fmt.Println("关闭数据库")
}
func Test01() {
TestStructs := []Test{
{Id: 1, Types: 1, CommitId: 3, CmdArgv: "123", Addition: "456"},
{Id: 2, Types: 2, CommitId: 1, CmdArgv: "123", Addition: "456"},
}
_, err := db.NamedExec(`INSERT INTO test values (:id, :type, :commitId, :cmdArgv, :addition)`, TestStructs)
if err != nil {
fmt.Println(err)
panic(err)
} else {
fmt.Println("success")
}
}
func main() {
Test01()
defer Finalizer()
}
报错信息如下:
这里使用的 sqlx 版本是 1.3.5,其他版本不一定有这个问题,这里简单说明一下哈。
看了这个报错信息,大概意思是期待 5 个参数,给了 10 个,寻思这个问题确实奇怪,不过好在 go 语言第三方库开源,这也是我比较喜欢 go 语言的一个地方。。那就检查看看呗。
首先,项目管理肯定要养成好的习惯,这里先简单介绍一下项目管理的设置。这里使用的 IDE 是 golang,利用 go mod 管理项目。
调试看看呗,这里是 45 行 panic 报错,那肯定是 42 行这里返回了错误信息,打个断点开始调,F7,F8 结合用。
进了几次,发现这里最后是利用 Exec 进行插入的,不过目前这个 q=INSERT INTO test values (?, ?, ?, ?, ?),感觉问题出在这里,按理说有两行数据,应该是 INSERT INTO test values (?, ?, ?, ?, ?)(?, ?, ?, ?, ?) 形式才对,那问题应该是出在这里。。
跟踪 q,跳到了 bindArray 函数中,最后跳到了 fixBound 函数,这里逻辑上也没有问题,有两行数据 bound 就是上面的 q,不修复的话 q 就是 INSERT INTO test values (?, ?, ?, ?, ?),修复后应该就没有问题。
但是这里没有修复,最后检查发现,问题出在 valuesReg 这个正则表达式上\)\s*(?i)VALUES\s*\(,目前的形式是 INSERT INTO test values (?, ?, ?, ?, ?),显然匹配不上,那直接把这个正则表达式改成\s*(?i)VALUES\s*\(,那估计应该没问题了
file:named.go
bindArray 函数中
if arrayLen > 1 {
bound = fixBound(bound, arrayLen)
}
var valuesReg = regexp.MustCompile(`\)\s*(?i)VALUES\s*\(`)
func fixBound(bound string, loop int) string {
loc := valuesReg.FindStringIndex(bound)
// defensive guard when "VALUES (...)" not found
if len(loc) < 2 {
return bound
}
openingBracketIndex := loc[1] - 1
index := findMatchingClosingBracketIndex(bound[openingBracketIndex:])
// defensive guard. must have closing bracket
if index == 0 {
return bound
}
closingBracketIndex := openingBracketIndex + index + 1
var buffer bytes.Buffer
buffer.WriteString(bound[0:closingBracketIndex])
for i := 0; i < loop-1; i++ {
buffer.WriteString(",")
buffer.WriteString(bound[openingBracketIndex:closingBracketIndex])
}
buffer.WriteString(bound[closingBracketIndex:])
return buffer.String()
}
找到上面图中默认的 GOPATH 路径,进到$GOPATH/pkg/mod/github.com/jmoiron/sqlx@v1.3.5/,找到 named.go 文件修改 valuesReg 正则表达式的值,再试试。
解决方法
方法一:修改库文件中的 valuesReg 正则表达式的值
修改 sqlx 库文件的正则表达式的值
发现没问题了。。。其实这里还有一种办法,
方法二:不修改库,修改sql语句
不修改 sqlx 库文件的值,修改 sql 语句,把所有字段列出来
个人认为虽然方法二更好,但是方法一更具有价值,go 语言优势之一就是库开源,遇到问题,不应该立刻就去网上找答案,网上的答案不一定就和自己的情况一样,如版本不一定,结果可能就完全不一样,所以应该利用开源的优势,调试不熟悉也没有关系,实在不行在库文件中多加几个 fmt.Println,看看到底是那里有问题。而且,第三库和 go 语言的自带库都是很好的资源 (如自带库中双链表 container/list/list.go,建议看看,写的真的很好)。
最后,也是收获满满。今天又是快乐的一天。。
本文讲述了使用go语言的sqlx库进行数据库插入时遇到的问题,主要涉及到NamedExec方法和valuesReg正则表达式的匹配。作者提供了两种解决方案:一是修改库文件中的正则表达式,二是调整SQL语句。强调了在遇到问题时利用开源库进行调试的价值。
742

被折叠的 条评论
为什么被折叠?



