zoukankan      html  css  js  c++  java
  • go标准库的学习-database/sql/driver

    参考:https://studygolang.com/pkgdoc

    1》导入方式:

    import "database/sql/driver"

    driver包定义了应被数据库驱动实现的接口,这些接口会被sql包使用。 

    绝大多数代码应使用sql包。

    2》driver.Driver - 在database/sql/driver中

    Driver是一个数据库驱动的接口,其定义了一个Open(name string)方法,该方法返回一个数据库的Conn接口:

    type Driver interface {
        // Open返回一个新的与数据库的连接,参数name的格式是驱动特定的。
        //
        // Open可能返回一个缓存的连接(之前关闭的连接),但这么做是不必要的;
        // sql包会维护闲置连接池以便有效的重用连接。
        //
        // 返回的连接同一时间只会被一个go程使用。
        Open(name string) (Conn, error)
    }

    因为返回的连接同一时间只会被一个go程使用,所以返回的Conn只能用来进行一次goroutine操作,即不能把这个Conn应用于Go的多个goroutine中,否则会出现错误,如:

    go goroutineA(Conn) //执行查询操作
    go goroutineB(Conn) //执行插入操作

    这样的代码会使Go不知某个操作到底是由哪个goroutine发起的从而导致数据混乱。即可能会讲goroutineA里面执行的查询操作的结果返回给goroutineB,从而让goroutineB将此结果当成自己执行的插入数据

    3》driver.Conn - 在database/sql/driver中

    Conn是一个数据连接的接口定义。这个Conn只能应用在一个goroutine中,如上所说。

    type Conn interface {
        // Prepare返回一个准备好的、绑定到该连接的状态。
        Prepare(query string) (Stmt, error)
    
        // Close作废并停止任何现在准备好的状态和事务,将该连接标注为不再使用。
        //
        // 因为sql包维护着一个连接池,只有当闲置连接过剩时才会调用Close方法,
        // 驱动的实现中不需要添加自己的连接缓存池。
        Close() error
    
        // Begin开始并返回一个新的事务。
        Begin() (Tx, error)
    }

    Prepare函数返回与当前连接相关的SQL语句的准备状态,可以进行查询、删除等操作

    Close函数关闭当前的连接,执行释放连接拥有的资源等清理工作。因为驱动实现了database/sql中建议的conn pool,所以不用再去实现缓存conn之类的,这样会更容易引起问题

    Begin函数返回一个代表事务处理的Tx,通过它你可以进行查询、更新等操作,或者对事务进行回滚、递交

    4》driver.Stmt - 在database/sql/driver中

    Stmt是一种准备好的状态,绑定到一个Conn中,并只能应用在一个goroutine中。

    type Stmt interface {
        // Close关闭Stmt。
        //
        // 和Go1.1一样,如果Stmt被任何查询使用中的话,将不会被关闭。
        Close() error
    
        // NumInput返回占位参数的个数。
        //
        // 如果NumInput返回值 >= 0,sql包会提前检查调用者提供的参数个数,
        // 并且会在调用Exec或Query方法前返回数目不对的错误。
        //
        // NumInput可以返回-1,如果驱动占位参数的数量不知时。
        // 此时sql包不会提前检查参数个数。
        NumInput() int
    
        // Exec执行查询,而不会返回结果,如insert或update。
        Exec(args []Value) (Result, error)
    
        // Query执行查询并返回结果,如select。
        Query(args []Value) (Rows, error)
    }

    Close函数关闭当前的连接状态,但是如果当前正在执行query,query还是会有效地返回rows数据

    Exec函数执行Conn的Prepare准备好的sql,传入参数执行update/insert等操作,返回Result数据

    Query函数执行Conn的Prepare准备好的sql,传入需要的参数执行select操作,返回Rows结果集

    5》driver.Tx - 在database/sql/driver中

    事务处理一般就两个过程,递交或回滚,即下面的两个函数:

    type Tx interface {
        Commit() error
        Rollback() error
    }

    6》driver.Execer - 在database/sql/driver中

    这是一个Conn可选择实现的接口

    type Execer interface {
        Exec(query string, args []Value) (Result, error)
    }

    如果一个Conn未实现Execer接口,sql包的DB.Exec会首先准备一个查询(即调用Prepare返回Stmt),执行状态(即执行Stmt的Exec函数),然后关闭状态(即关闭Stmt)。Exec可能会返回ErrSkip。

    7》driver.Result

    这是是执行Update/insert等操作返回的结果接口定义

    type Result interface {
        // LastInsertId返回insert等命令后数据库自动生成的ID
        LastInsertId() (int64, error)
    
        // RowsAffected返回被查询影响的行数
        RowsAffected() (int64, error)
    }

    8》driver.Rows

    Rows是执行查询返回的结果集接口定义

    type Rows interface {
        // Columns返回各列的名称,列的数量可以从切片长度确定。
        // 如果某个列的名称未知,对应的条目应为空字符串。
        Columns() []string
    
        // Close关闭Rows。
        Close() error
    
        // 调用Next方法以将下一行数据填充进提供的切片中,即返回下一条数据,并把数据返回给dest。
        // 提供的切片必须和Columns返回的切片长度相同。
        //
        // 切片dest可能被填充同一种驱动Value类型,但字符串除外;即dest里面的元素必须是driver.Vlaue的值,除了string。
        // 所有string值都必须转换为[]byte。
        //
        // 当没有更多行时,Next应返回io.EOF。
        Next(dest []Value) error
    }

    Columns函数返回查询数据库表的字段信息,返回的slice和sql查询的字段一一对应,而不是返回整个表的所有字段

    9》driver.RowsAffected

    type RowsAffected int64

    RowsAffected其实就是int64的别名,但是它实现了Result接口,用来底层实现Result的表示方式

    RowsAffected实现了Result接口,用于insert或update操作,这些操作会修改零到多行数据。

    func (RowsAffected) LastInsertId

    func (RowsAffected) LastInsertId() (int64, error)

    func (RowsAffected) RowsAffected

    func (v RowsAffected) RowsAffected() (int64, error)


    10》driver.Value

    type Value interface{}

    Value其实就是一个空接口,它可以容纳任何数据

    driver.Value是驱动必须能够操作的Value,所以Value要么是nil,要么是下面的任意一种:

    int64
    float64
    bool
    []byte
    string   [*] Rows.Next不会返回该类型值
    time.Time

    11》driver.ValueConverter

    ValueConverter接口定义了一个如何把一个普通值转化成driver.Value的接口

    type ValueConverter interface {
        // ConvertValue将一个值转换为驱动支持的Value类型
        ConvertValue(v interface{}) (Value, error)
    }

    ValueConverter接口提供了ConvertValue方法。

    driver包提供了各种ValueConverter接口的实现,以保证不同驱动之间的实现和转换的一致性。ValueConverter接口有如下用途:

    • 转换sql包提供的driver.Value类型值到数据库指定列的类型,并保证它的匹配,例如保证某个int64值满足一个表的uint16列。
    • 转换数据库提供的值(即数据库查询结果)成driver.Value类型。
    • 在Scan函数中被sql包用于将driver.Value类型转换为用户定义的类型。

    12》driver.Valuer

    type Valuer interface {
        // Value返回一个驱动支持的Value类型值
        Value() (Value, error)
    }

    Valuer接口定义了一个返回driver.Value的方法

    很多类型都实现了这个Value方法,用来实现自身与driver.Value的转换

    13》ColumnConverter

    type ColumnConverter

    type ColumnConverter interface {
        // ColumnConverter返回指定列的ValueConverter
        // 如果该列未指定类型,或不应特殊处理,应返回DefaultValueConverter
        ColumnConverter(idx int) ValueConverter
    }

    如果Stmt有自己的列类型,可以实现ColumnConverter接口,返回值可以将任意类型转换为驱动的Value类型。

    14》变量

    1.Bool

    var Bool boolType

    Bool是ValueConverter接口值,用于将输入的值转换为布尔类型。使用方式为driver.Bool(1),会返回true

    转换规则如下:

    - 布尔类型:不做修改
    - 整数类型:
         1 为真
         0 为假
         其余整数会导致错误
    - 字符串和[]byte:与strconv.ParseBool的规则相同
    - 所有其他类型都会导致错误

    其实现源码为:

    var Bool boolType
    
    type boolType struct{}
    
    var _ ValueConverter = boolType{}
    
    func (boolType) String() string { return "Bool" }
    
    func (boolType) ConvertValue(src interface{}) (Value, error) {
        switch s := src.(type) { //首先查看输入的src值的类型
        case bool: //如果输入的是bool类型,则直接输出即可
            return s, nil
        case string: //如果是其他类型,则要将其转成Bool类型
            b, err := strconv.ParseBool(s)
            if err != nil {
                return nil, fmt.Errorf("sql/driver: couldn't convert %q into type bool", s)
            }
            return b, nil
        case []byte:
            b, err := strconv.ParseBool(string(s))
            if err != nil {
                return nil, fmt.Errorf("sql/driver: couldn't convert %q into type bool", s)
            }
            return b, nil
        }
    
        //除了上面的几种类型,如果是下面的类型,只有当其值为1或0时能够将其转成bool类型
        sv := reflect.ValueOf(src)
        switch sv.Kind() {
        case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
            iv := sv.Int()
            if iv == 1 || iv == 0 {
                return iv == 1, nil
            }
            return nil, fmt.Errorf("sql/driver: couldn't convert %d into type bool", iv)
        case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
            uv := sv.Uint()
            if uv == 1 || uv == 0 {
                return uv == 1, nil
            }
            return nil, fmt.Errorf("sql/driver: couldn't convert %d into type bool", uv)
        }
        //除了上面的类型,如果输入的src是其他的类型,则会报错
        return nil, fmt.Errorf("sql/driver: couldn't convert %v (%T) into type bool", src, src)
    }

    2.Int32

    var Int32 int32Type

    Int32是一个ValueConverter接口值,用于将值转换为int64类型,会尊重int32类型的限制。

    3.String

    var String stringType

    String是一个ValueConverter接口值,用于将值转换为字符串。如果值v是字符串或者[]byte类型,不会做修改,如果值v是其它类型,会转换为fmt.Sprintf("%v", v)。

    4.DefaultParameterConverter

    var DefaultParameterConverter defaultConverter

    DefaultParameterConverter是ValueConverter接口的默认实现,当一个Stmt没有实现ColumnConverter时,就会使用它。 

    如果值value满足函数IsValue(value)为真,DefaultParameterConverter直接返回 value。否则,整数类型会被转换为int64,浮点数转换为float64,字符串转换为[]byte。其它类型会导致错误。

    5.ResultNoRows

    var ResultNoRows noRows

    ResultNoRows是预定义的Result类型值,用于当一个DDL命令(如create table)成功时被驱动返回。它的LastInsertId和RowsAffected方法都返回错误

    举例:

    package main 
    import(
        "fmt"
        "reflect"
        "time"
        "database/sql/driver"
    )
    
    type valueConverterTest struct {
        c   driver.ValueConverter
        in  interface{}
        out interface{}
        err string
    }
    
    var now = time.Now()
    var answer int64 = 42
    
    type (
        i  int64
        f  float64
        b  bool
        bs []byte
        s  string
        t  time.Time
        is []int
    )
    
    var valueConverterTests = []valueConverterTest{
        {driver.Bool, "true", true, ""},
        {driver.Bool, "True", true, ""},
        {driver.Bool, []byte("t"), true, ""},
        {driver.Bool, true, true, ""},
        {driver.Bool, "1", true, ""},
        {driver.Bool, 1, true, ""},
        {driver.Bool, int64(1), true, ""},
        {driver.Bool, uint16(1), true, ""},
        {driver.Bool, "false", false, ""},
        {driver.Bool, false, false, ""},
        {driver.Bool, "0", false, ""},
        {driver.Bool, 0, false, ""},
        {driver.Bool, int64(0), false, ""},
        {driver.Bool, uint16(0), false, ""},
        {c: driver.Bool, in: "foo", err: "sql/driver: couldn't convert "foo" into type bool"},
        {c: driver.Bool, in: 2, err: "sql/driver: couldn't convert 2 into type bool"},
        {driver.DefaultParameterConverter, now, now, ""},
        {driver.DefaultParameterConverter, (*int64)(nil), nil, ""},
        {driver.DefaultParameterConverter, &answer, answer, ""},
        {driver.DefaultParameterConverter, &now, now, ""},
        {driver.DefaultParameterConverter, i(9), int64(9), ""},
        {driver.DefaultParameterConverter, f(0.1), float64(0.1), ""},
        {driver.DefaultParameterConverter, b(true), true, ""},
        {driver.DefaultParameterConverter, bs{1}, []byte{1}, ""},
        {driver.DefaultParameterConverter, s("a"), "a", ""},
        {driver.DefaultParameterConverter, is{1}, nil, "unsupported type main.is, a slice of int"},
    }
    
    
    
    func main() {
        for i, tt := range valueConverterTests {
            out, err := tt.c.ConvertValue(tt.in)
            goterr := ""
            if err != nil {
                goterr = err.Error()
            }
            if goterr != tt.err {
                fmt.Printf("test %d: %T(%T(%v)) error = %q; want error = %q
    ",
                    i, tt.c, tt.in, tt.in, goterr, tt.err)
            }
            if tt.err != "" {
                continue
            }
            if !reflect.DeepEqual(out, tt.out) {
                fmt.Printf("test %d: %T(%T(%v)) = %v (%T); want %v (%T)
    ",
                    i, tt.c, tt.in, tt.in, out, out, tt.out, tt.out)
            }
        }
    }

    6.错误ErrBadConn

    var ErrBadConn = errors.New("driver: bad connection")

    ErrBadConn应被驱动返回,以通知sql包一个driver.Conn处于损坏状态(如服务端之前关闭了连接),sql包会重启一个新的连接。

    为了避免重复的操作,如果数据库服务端执行了操作,就不应返回ErrBadConn。即使服务端返回了一个错误。

    7.错误ErrSkip

    var ErrSkip = errors.New("driver: skip fast-path; continue as if unimplemented")

    ErrSkip可能会被某些可选接口的方法返回,用于在运行时表明快速方法不可用,sql包应像未实现该接口的情况一样执行。ErrSkip只有文档显式说明的地方才支持,如driver.Execer。

    15》其他函数

    func IsValue

    func IsValue(v interface{}) bool

    IsValue报告v是否是合法的Value类型参数。和IsScanValue不同,IsValue接受字符串类型。

    func IsScanValue

    func IsScanValue(v interface{}) bool

    IsScanValue报告v是否是合法的Value扫描类型参数。和IsValue不同,IsScanValue不接受字符串类型。

  • 相关阅读:
    Git 分支创建,合并, 分支切换, 分支拉取,提交
    Win7 Nodejs 安装
    .ssh github
    xxxx.IronManager was loaded by com.taobao.pandora.boot.loader.XxxxClassLoader@xxx,it should be loaded by Pandora Container...与摒弃引进别的项目的一些冲突包
    推荐一波微软家的浏览器:EDGE
    谷歌浏览器新功能 Copy Declaration
    微信支付回调数据接收不完整解决方案
    开源物联网框架EasyIot(适用于快递柜&售货机)
    开源物联网框架EasyIot场景落地(适用于快递柜、储物柜)
    海康摄像头音频方案(播放音频文件+语音对讲+语音转发)支持window/Linuxjava版本
  • 原文地址:https://www.cnblogs.com/wanghui-garcia/p/10405601.html
Copyright © 2011-2022 走看看