这是我参与「第三届青训营 -后端场」笔记创作活动的第2篇笔记。
基础用法
import (
"database/sql"//这个包里有连接池,一些接口用于连接数据库和操作数据库
_"github.com/go-sql-driver/mysql"
)
func main(){
db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/hello") //使用driver+DSN初始化DB连接
rows, err := db.Query("select id, name from users where id = ?", 1) //执行一条sql,通过rows取回返回的数据
if err != nil{
//xxx
}
//处理完释放资源
defer func(){
err = rows.Close()
}
//处理获得的数据,错误处理
var users []User
for rows.Next(){ //不断获得下一条数据
vae user User
err := rows.Scan(&user.ID, &user.Name) //从rows中提取
if errr != nil{
// ...
}
users = append(users, user) //加到user
}
if rows.Err() != nil{
//...
}
func init(){
sql.Register("mysql", &MySQLDriver{})
}
}
这里import了两个包,一个是database/sql,这个包是Go原生的负责数据库操作的包,定义了很多连接和操作数据库实例的接口(定义了很多方法)。另一个包是数据库提供商负责提供的数据库驱动包github.com/go-sql-driver/mysql,这个包去实现了Go原生database/sql包中接口定义的方法(实现了操作某数据库实例的具体细节)。
这样对于Go用户来说,我只要调用我接口定义的方法,import入MySQL提供的driver包,那么我就无须关注底层的具体实现,从而可以使用Go的API与MySQL进程进行通信,发送CRUD请求,然后再由MySQL进程去管理MySQL磁盘文件,持久化数据变更。
2、设计原理
操作过程的伪实现
Driver连接接口
Driver连接接口2
基础概念
DB连接的几种类型
- 直接连接/Conn
- 预编译/Stmt
- 事务/Tx
处理返回数据的几种方式
- Exec/ExecContext -> Result
- Query / QueryContext-> Rows (Columns)
- QueryRow / QueryRowContext -> Row (Rows简化)
操作接口
增删查改
新建数据库
```
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int NOT NULL AUTO_INCREMENT COMMENT 'id',
`name` varchar(255) NOT NULL COMMENT '姓名',
`age` int NOT NULL COMMENT '年龄',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb3;
-- ----------------------------
-- Records of user
-- ----------------------------
BEGIN;
INSERT INTO `user` VALUES (1, '小明', 12);
INSERT INTO `user` VALUES (2, '小红', 15);
INSERT INTO `user` VALUES (3, '小蓝', 11);
COMMIT;
```
查询
单行查询
```
func getUserById(id int) error {
sqlStr := "select id, name, age from user where id = ?"
var user User
// 这里的1是参数,用于查询id=1的行记录
if err := db.QueryRow(sqlStr, 1).Scan(&user.id, &user.name, &user.age); err != nil {
return err
}
fmt.Println(user.id, user.name, user.age)
return nil
}
```
多行查询
func queryUsers() error {
sqlStr := "select id, name, age from user where id > ?"
rows, err := db.Query(sqlStr, 1)
if err != nil {
return err
}
// 关闭rows释放持有的数据库链接
defer rows.Close()
// 循环读取结果集中的数据
for rows.Next() {
var user User
if err := rows.Scan(&user.id, &user.name, &user.age); err != nil {
return err
}
fmt.Printf("id:%d name:%s age:%d\n", user.id, user.name, user.age)
}
return nil
}
```
插入
```
func insertUser() error {
sqlStr := "insert into user(name, age) values (?,?)"
ret, err := db.Exec(sqlStr, "小黄", 17)
if err != nil {
return err
}
theID, err := ret.LastInsertId() // 新插入数据的id
if err != nil {
return err
}
fmt.Printf("insert success, the id is %d.\n", theID)
return nil
}
```
更新
```
func updateUser() error {
sqlStr := "update user set age=? where id = ?"
ret, err := db.Exec(sqlStr, 20, 3)
if err != nil {
return err
}
n, err := ret.RowsAffected() // 操作影响的行数
if err != nil {
return err
}
fmt.Printf("update success, affected rows:%d\n", n)
return nil
}
```
删除
```
// 删除数据
func deleteUser() error {
sqlStr := "delete from user where id = ?"
ret, err := db.Exec(sqlStr, 5)
if err != nil {
return err
}
n, err := ret.RowsAffected() // 操作影响的行数
if err != nil {
return err
}
fmt.Printf("delete success, affected rows:%d\n", n)
return nil
}
```