DATABASE/SQL与GORM | 青训营笔记

275 阅读4分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的第3篇笔记

1.database/sql

在Go语言标准库中提供了进行数据库操作的 database/sql 库

1.1:基本用法

(1)import实现,使用driver+DSN初始化DB连接

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")
}

(2)执行一条sql通过rows取回返回的数据并关闭链接

func main() {
	db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/hello")
	rows, err := db.Query("select id, name from users where id=?", 2)
	if err != nil {
		//输出错误
	}
	defer rows.Close()
}

(3)数据,错误处理

var users []user
	for rows.Next() {
		var user User
		err:=rows.Scan(&user.ID,&user.Name)
		if err!=nil {
			//...
		}
		users=append(users,user)
	}
	if rows.Err()!=nil{
		//...
	}
}

defer func(){
	err=rows.Close()
}

1.2:设计原理

(1)池化技术: 池化技术 (Pool) 是一种很常见的编程技巧,在请求量大时能明显优化应用性能,降低系统频繁建连的资源开销,数据库连接池、线程池、对象池等,它们的特点都是将 “昂贵的”、“费时的” 的资源维护在一个特定的 “池子” 中,规定其最小连接数、最大连接数、阻塞队列等配置,方便进行统一管理和复用。

(2)DB连接类型:

直接连接: 直接连接Coon

预编译Stmt:

(1)stmt对于多次执行的语句比直接执行快,在查询前进行准备是Go语言中的习惯用法,多次使用的查询语句应当进行准备。准备查询的结果是一个准备好的语句,语句中可以包含执行时所需参数的占位符。准备查询比拼字符串的方式好很多,它可以转义参数,避免SQL注入。同时,准备查询对于一些数据库也省去了解析和生成执行计划的开销,有利于性能。

(2)使用方法

num := 12 
stmt, err := db.Prepare("SELECT name FROM users WHERE num = ?")
if err != nil {
  log.Fatal(err)
}
rows, err := stmt.Query(num)

(3)底层:在数据库层面,预编译Stmt是与单个数据库连接绑定的。通常的流程是:客户端向服务器发送带有占位符的查询语句用于准备,服务器返回一个语句ID,客户端在实际执行时,只需要传输语句ID和相应的参数。因此准备语句无法在连接之间共享,当使用新的数据库连接时,必须重新准备。

事务Tx:

(1)什么是事务

事务是关系型数据库的核心特性。Go中事务(Tx)是一个持有数据库连接的对象,它允许用户在同一个连接上执行上面的各类操作。

(2)使用方法

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

(3)基本操作

通过db.Begin()来开启一个事务,Begin方法会返回一个事务对象Tx。在结果变量Tx上调用Commit()或者Rollback()方法会提交或回滚变更,并关闭事务。

(4)底层逻辑

在底层,Tx会从连接池中获得一个连接并在事务过程中保持对它的独占。事务对象Tx上的方法与数据库对象sql.DB的方法对应,事务对象也可以准备查询,由事务创建的准备语句会显式绑定到创建它的事务。

(3) 处理返回数据的几种方式:

ExecContext

ExecerContext是可以被Conn实现的、可选的接口,如果Conn并没有实现ExecerContext接口,那sql包的DB.Exec将会向后调用Excer,如果Conn也没有实现Execer接口,DB.Exec将首先prepare查询,执行语句、然后关闭语句。ExecerContext 可能返回 ErrSkip错误。ExecerContext必须认真对待context的超时,当context被取消时,需要返回。

type ExecerContext interface {
	ExecContext(ctx context.Context, query string, args []NamedValue) (Result, error)
}

QueryContext读取所有

QueryerContext 是一个可选的接口,Conn可能会实现它,如果Conn没有实现QueryerContext,那么sql包在执行DB.Query会降级调用Queryer;如果Conn也没有实现Queryer,DB.Query 首先会prepare一个查询语句,然后执行这个语句,然后再关闭它QueryerContext可能会返回 ErrSkip。QueryerContext必须认真对待context的超时,当context被cancel掉时,需要返回。

type QueryerContext interface {
	QueryContext(ctx context.Context, query string, args []NamedValue) (Rows, error)
}

QueryRowContext只读取一行

2.GORM

orm:

ORM(Object Relational Mapping)框架采用元数据来描述对象与关系映射的细节,元数据一般采用XML格式,并且存放在专门的对象一映射文件中。简单理解为一种框架的格式。它的作用是在关系型数据库和对象之间作一个映射。

GORM:

Golang写的orm库。

2.1:基本用法

(1)连接数据库

package main
import (
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)
func main() {
        用户名:密码@tcp(ip:port)/数据库?charset=utf8mb4&parseTime=True&loc=Local
	dsn := "root:root123@tcp(127.0.0.1:3306)/test_gorm?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		panic(err)
	}
}

(2)增删改查

        // 增
	db.Create(&User{
	   Name: "a",
	   Age:  22,
	   Addr: "北京市",
	})
        
        // 删
	db.Delete(&user)
        
        // 改
	user.Name = "b"
	db.Save(&user)
	fmt.Println(user)

	// 查
	var user User
	db.First(&user)
	fmt.Println(user)