database/sql 包是 Go 语言标准库中用于数据库操作的核心包之一。它提供了一种通用的接口,使你可以连接和操作不同类型的关系型数据库,包括 MySQL、PostgreSQL、SQLite、Microsoft SQL Server 等。以下是 database/sql 包的一些关键概念和功能:
import("database/sql"_"github.com/go-sql-driver/mysql""fmt")funcmain(){// 打开与 MySQL 数据库的连接db,err:=sql.Open("mysql","用户名:密码@tcp(localhost:3306)/数据库名")iferr!=nil{fmt.Println("数据库连接失败:",err)return}deferdb.Close()// 确保在函数退出时关闭数据库连接// 现在可以使用 db 执行 SQL 查询和操作}
funcRegister(namestring,driverdriver.Driver){driversMu.Lock()// 加锁deferdriversMu.Unlock()ifdriver==nil{panic("sql: Register driver is nil")}if_,dup:=drivers[name];dup{panic("sql: Register called twice for driver "+name)}drivers[name]=driver}
// 2.多行查询:使用db.Query()执行一次查询,返回多行结果(即Rows),一般用于执行select命令。参数args表示query中的占位参数。funcqueryMultiRowData(){sqlStr:="select id, name, age from user where id > ?"rows,err:=db.Query(sqlStr,0)iferr!=nil{fmt.Printf("query failed, err:%v\n",err)return}// 非常重要:关闭rows释放持有的数据库链接deferrows.Close()// 循环读取结果集中的数据forrows.Next(){varuUsererr:=rows.Scan(&u.Id,&u.Name,&u.Age)iferr!=nil{fmt.Printf("scan failed, err:%v\n",err)return}fmt.Printf("id:%d name:%s age:%d\n",u.Id,u.Name,u.Age)}}
3.插入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 3.插入数据:插入、更新和删除操作都使用Exec方法。funcinsertRowData(){sqlStr:="insert into user(name, age) values (?,?)"ret,err:=db.Exec(sqlStr,"王五",38)// 第一个返回值类型为Result,保存了执行结果的信息(自增ID、影响行数)iferr!=nil{fmt.Printf("insert failed, err:%v\n",err)return}theID,err:=ret.LastInsertId()// 新插入数据的idiferr!=nil{fmt.Printf("get lastinsert ID failed, err:%v\n",err)return}fmt.Printf("insert success, the id is %d.\n",theID)}
4.更新
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 4.更新数据funcupdateRowData(){sqlStr:="update user set age=? where name = ?"ret,err:=db.Exec(sqlStr,30,"Tom")iferr!=nil{fmt.Printf("update failed, err:%v\n",err)return}n,err:=ret.RowsAffected()// 操作影响的行数iferr!=nil{fmt.Printf("get RowsAffected failed, err:%v\n",err)return}fmt.Printf("update success, affected rows:%d\n",n)}
5.删除
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 5.删除数据funcdeleteRowData(){sqlStr:="delete from user where name = ?"ret,err:=db.Exec(sqlStr,"王五")iferr!=nil{fmt.Printf("delete failed, err:%v\n",err)return}n,err:=ret.RowsAffected()// 操作影响的行数iferr!=nil{fmt.Printf("get RowsAffected failed, err:%v\n",err)return}fmt.Printf("delete success, affected rows:%d\n",n)}
// 事务操作示例functransactionDemo(){tx,err:=db.Begin()// 开启事务iferr!=nil{iftx!=nil{tx.Rollback()// 回滚}fmt.Printf("begin trans failed, err:%v\n",err)return}sqlStr1:="Update user set age=30 where id=?"ret1,err:=tx.Exec(sqlStr1,2)iferr!=nil{tx.Rollback()// 回滚fmt.Printf("exec sql1 failed, err:%v\n",err)return}affRow1,err:=ret1.RowsAffected()iferr!=nil{tx.Rollback()// 回滚fmt.Printf("exec ret1.RowsAffected() failed, err:%v\n",err)return}sqlStr2:="Update user set age=40 where id=?"ret2,err:=tx.Exec(sqlStr2,3)iferr!=nil{tx.Rollback()// 回滚fmt.Printf("exec sql2 failed, err:%v\n",err)return}affRow2,err:=ret2.RowsAffected()iferr!=nil{tx.Rollback()// 回滚fmt.Printf("exec ret1.RowsAffected() failed, err:%v\n",err)return}fmt.Println(affRow1,affRow2)ifaffRow1==1&&affRow2==1{fmt.Println("事务提交啦...")tx.Commit()// 提交事务}else{tx.Rollback()fmt.Println("事务回滚啦...")}fmt.Println("exec trans success!")}
vardb*sqlx.DBfuncinitDB()(errerror){dsn:="user:password@tcp(127.0.0.1:3306)/sql_test?charset=utf8mb4&parseTime=True"// 也可以使用MustConnect连接不成功就panicdb,err=sqlx.Connect("mysql",dsn)iferr!=nil{fmt.Printf("connect DB failed, err:%v\n",err)return}db.SetMaxOpenConns(20)db.SetMaxIdleConns(10)return}
查询
查询单行数据示例代码如下:
1
2
3
4
5
6
7
8
9
10
11
// 查询单条数据示例funcqueryRowDemo(){sqlStr:="select id, name, age from user where id=?"varuusererr:=db.Get(&u,sqlStr,1)iferr!=nil{fmt.Printf("get failed, err:%v\n",err)return}fmt.Printf("id:%d name:%s age:%d\n",u.ID,u.Name,u.Age)}
查询多行数据示例代码如下:
1
2
3
4
5
6
7
8
9
10
11
// 查询多条数据示例funcqueryMultiRowDemo(){sqlStr:="select id, name, age from user where id > ?"varusers[]usererr:=db.Select(&users,sqlStr,0)iferr!=nil{fmt.Printf("query failed, err:%v\n",err)return}fmt.Printf("users:%#v\n",users)}
// 插入数据funcinsertRowDemo(){sqlStr:="insert into user(name, age) values (?,?)"ret,err:=db.Exec(sqlStr,"沙河小王子",19)iferr!=nil{fmt.Printf("insert failed, err:%v\n",err)return}theID,err:=ret.LastInsertId()// 新插入数据的idiferr!=nil{fmt.Printf("get lastinsert ID failed, err:%v\n",err)return}fmt.Printf("insert success, the id is %d.\n",theID)}// 更新数据funcupdateRowDemo(){sqlStr:="update user set age=? where id = ?"ret,err:=db.Exec(sqlStr,39,6)iferr!=nil{fmt.Printf("update failed, err:%v\n",err)return}n,err:=ret.RowsAffected()// 操作影响的行数iferr!=nil{fmt.Printf("get RowsAffected failed, err:%v\n",err)return}fmt.Printf("update success, affected rows:%d\n",n)}// 删除数据funcdeleteRowDemo(){sqlStr:="delete from user where id = ?"ret,err:=db.Exec(sqlStr,6)iferr!=nil{fmt.Printf("delete failed, err:%v\n",err)return}n,err:=ret.RowsAffected()// 操作影响的行数iferr!=nil{fmt.Printf("get RowsAffected failed, err:%v\n",err)return}fmt.Printf("delete success, affected rows:%d\n",n)}
NamedExec
DB.NamedExec方法用来绑定SQL语句与结构体或map中的同名字段。
1
2
3
4
5
6
7
8
9
funcinsertUserDemo()(errerror){sqlStr:="INSERT INTO user (name,age) VALUES (:name,:age)"_,err=db.NamedExec(sqlStr,map[string]interface{}{"name":"七米","age":28,})return}
funcnamedQuery(){sqlStr:="SELECT * FROM user WHERE name=:name"// 使用map做命名查询rows,err:=db.NamedQuery(sqlStr,map[string]interface{}{"name":"七米"})iferr!=nil{fmt.Printf("db.NamedQuery failed, err:%v\n",err)return}deferrows.Close()forrows.Next(){varuusererr:=rows.StructScan(&u)iferr!=nil{fmt.Printf("scan failed, err:%v\n",err)continue}fmt.Printf("user:%#v\n",u)}u:=user{Name:"七米",}// 使用结构体命名查询,根据结构体字段的 db tag进行映射rows,err=db.NamedQuery(sqlStr,u)iferr!=nil{fmt.Printf("db.NamedQuery failed, err:%v\n",err)return}deferrows.Close()forrows.Next(){varuusererr:=rows.StructScan(&u)iferr!=nil{fmt.Printf("scan failed, err:%v\n",err)continue}fmt.Printf("user:%#v\n",u)}}
functransactionDemo2()(errerror){tx,err:=db.Beginx()// 开启事务iferr!=nil{fmt.Printf("begin trans failed, err:%v\n",err)returnerr}deferfunc(){ifp:=recover();p!=nil{tx.Rollback()panic(p)// re-throw panic after Rollback}elseiferr!=nil{fmt.Println("rollback")tx.Rollback()// err is non-nil; don't change it}else{err=tx.Commit()// err is nil; if Commit returns error update errfmt.Println("commit")}}()sqlStr1:="Update user set age=20 where id=?"rs,err:=tx.Exec(sqlStr1,1)iferr!=nil{returnerr}n,err:=rs.RowsAffected()iferr!=nil{returnerr}ifn!=1{returnerrors.New("exec sqlStr1 failed")}sqlStr2:="Update user set age=50 where i=?"rs,err=tx.Exec(sqlStr2,5)iferr!=nil{returnerr}n,err=rs.RowsAffected()iferr!=nil{returnerr}ifn!=1{returnerrors.New("exec sqlStr1 failed")}returnerr}
// BatchInsertUsers3 使用NamedExec实现批量插入funcBatchInsertUsers3(users[]*User)error{_,err:=DB.NamedExec("INSERT INTO user (name, age) VALUES (:name, :age)",users)returnerr}
关于sqlx.In这里再补充一个用法,在sqlx查询语句中实现In查询和FIND_IN_SET函数。即实现SELECT * FROM user WHERE id in (3, 2, 1);和SELECT * FROM user WHERE id in (3, 2, 1) ORDER BY FIND_IN_SET(id, '3,2,1');。
in查询
查询id在给定id集合中的数据。
1
2
3
4
5
6
7
8
9
10
11
12
13
// QueryByIDs 根据给定ID查询funcQueryByIDs(ids[]int)(users[]User,errerror){// 动态填充idquery,args,err:=sqlx.In("SELECT name, age FROM user WHERE id IN (?)",ids)iferr!=nil{return}// sqlx.In 返回带 `?` bindvar的查询语句, 我们使用Rebind()重新绑定它query=DB.Rebind(query)err=DB.Select(&users,query,args...)return}
in查询和FIND_IN_SET函数
查询id在给定id集合的数据并维持给定id集合的顺序。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// QueryAndOrderByIDs 按照指定id查询并维护顺序funcQueryAndOrderByIDs(ids[]int)(users[]User,errerror){// 动态填充idstrIDs:=make([]string,0,len(ids))for_,id:=rangeids{strIDs=append(strIDs,fmt.Sprintf("%d",id))}query,args,err:=sqlx.In("SELECT name, age FROM user WHERE id IN (?) ORDER BY FIND_IN_SET(id, ?)",ids,strings.Join(strIDs,","))iferr!=nil{return}// sqlx.In 返回带 `?` bindvar的查询语句, 我们使用Rebind()重新绑定它query=DB.Rebind(query)err=DB.Select(&users,query,args...)return}