Database/SQL学习 | 青训营笔记

229 阅读3分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的第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、设计原理

图片.png

操作过程的伪实现

图片.png

Driver连接接口

图片.png

图片.png

Driver连接接口2

图片.png

图片.png

基础概念

DB连接的几种类型

  1. 直接连接/Conn
  2. 预编译/Stmt
  3. 事务/Tx

处理返回数据的几种方式

  1. Exec/ExecContext -> Result
  2. Query / QueryContext-> Rows (Columns)
  3. QueryRow / QueryRowContext -> Row (Rows简化)

操作接口

图片.png

图片.png

增删查改

新建数据库

```
-- ----------------------------
-- 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
}
```