Blog.1 database.sql.driver
来源:SegmentFault
时间:2023-01-11 10:28:52 248浏览 收藏
本篇文章主要是结合我之前面试的各种经历和实战开发中遇到的问题解决经验整理的,希望这篇《Blog.1 database.sql.driver》对你有很大帮助!欢迎收藏,分享给更多的需要的朋友学习~
在事务操作中,要求事务的各个阶段都使用一个
// 包driver定义了数据驱动要实现的接口,具体的实现会在包sql中用到。 // // 更多还是使用包sql中的代码 package driver import ( "context" "errors" "reflect" ) // Value必须是一个驱动可以处理的值、NamedValueChecker接口能够处理的类型 // 或者下面这些类型的实例 // // int64 // float64 // bool // []byte // string // time.Time // // 如果驱动支持游标,返回值可能也实现Rows接口。举例,当用户 // 执行"select cursor(select * from my_table) from dual"。 // 如果返回的Rows被Close掉了,游标指向的数据也会被Close掉。 type Value interface{} // NameValue 同时包括name和value type NamedValue struct { // 如果Name不为空,它应该被用于参数标识符,而非序号位置。 // // Name 没有符号前缀 Name string // 参数从1开始的序号位置,并且总是被设置 Ordinal int // Value是参数值 Value Value } // Driver是一个必须被各个数据库driver实现的接口 // // 数据库驱动可以实现DriverContext来访问上下文,并且只解析一次连接池的名称, // 而非每个连接都解析一次。 // type Driver interface { // Open返回数据库的一个新连接,参数name是驱动特定格式的字符串 // // Open也可以返回一个缓存的连接(之前被close掉的),但这样 // 做其实没必要。sql包为了连接重复使用维护了一个空闲连接池 // // 返回的连接一次只被一个goroutinue中使用 Open(name string) (Conn, error) } // 如果Driver实现了DriverContext接口,那么sql.DB就会调用OpenConnector // 来获取一个Connector,调用Connector的Conn方法来获取每个需要的连接, // 以此代替调用Drive的Open方法。这样允许drivers仅解析一次name,同时提供 // 对每个连接上下文的访问 type DriverContext interface { // OpenConnector解析name的方式必须跟Driver.Open的方式保持一致 OpenConnector(name string) (Connector, error) } // 一个Connector表示一个固定配置的driver,能够创建任意数量的等效Conn, // 供多个goroutinue使用 // // 一个Connector能被传递给sql.OpenDB方法,去允许驱动实现自己的sql.DB。 // 或者通过调用DriverContext的OpenConnector方法,来返回一个Connector, // 这样允许驱动访问连接的上下文,避免频繁的解析驱动配置。 type Connector interface { // Connect返回一个数据库的连接 // Connect可能返回一个之前缓存的连接(之前被close掉了),但这样去 // 做其实是没必要的。sql包维护了一个高效重复使用的空闲连接池。 // // // 被提供的context.Context参数仅仅被用于创建连接的目的 //(看net.DialContext),不应该被存储或用于其他别的目的。 // // 返回的连接一次只能被一个goroutine使用 Connect(context.Context) (Conn, error) // Driver返回Connector的底层驱动,在sql.DB中,主要用于维护 // 驱动的扩展性 Driver() Driver } // ErrSkip 可能被一些可选接口的方法返回,用于在运行时标识该路径无效。 // 包sql应该继续去执行,就当类型没有实现这个接口一样。 // ErrSkip 只有在被明确说明后才会被支持 // var ErrSkip = errors.New("driver: skip fast-path; continue as if unimplemented") // 当驱动给sql包标识一个driver.Conn处于坏的状态时,ErrBadConn应该被返回( // 比如服务端已经关闭了这个连接),sql包已经使用一个新的连接进行重试 // // 为了避免重复操作,如果服务端可能已经完成操作的话,ErrBadConn不应该被返回。 // 即使服务端返回了一个错误,你也不应该返回ErrBadConn var ErrBadConn = errors.New("driver: bad connection") // Pinger是一个可选的接口,它可能会被Conn实现 // // 如果Conn没有实现Pinger接口,那么sql包的DB.Ping和DB.PingContext // 将会执行检查,是否至少存在一个可用连接 // // 如果Conn.Ping返回了ErrBadConn,DB.Ping 和 DB.PingContext将会从连接池中 // 将Conn溢出 type Pinger interface { Ping(ctx context.Context) error } // Execer 是一个可以被Conn实现的,可选的接口 // // 如果Conn实现了ExecerContext或Excer, // 包sql下的DB.Exec将首先prepare查询语句,执行然后关闭。 // // Exec可能返回ErrSkip错误 // // 弃用:Drivers应该实现ExecerContext接口来替代Execer type Execer interface { Exec(query string, args []Value) (Result, error) } // 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) } // Queryer 是一个可选的接口,Conn可能会实现它。 // // 如果Conn既没有实现QueryerContext,也没有实现Queryer // 那么sql包的DB.Query首先会prepare一个查询语句,然后执行语句,关闭语句 // // Query可能会返回 ErrSkip错误. // // 弃用:Drivers应该实现QueryerContext接口来替代Queryer type Queryer interface { Query(query string, args []Value) (Rows, error) } // 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) } // Conn是一条数据库的连接,它不能在多个goroutine中同时使用。 // // Conn被假定为是有状态的 type Conn interface { // Prepare返回一个准备好的语句,绑定到这个连接上 Prepare(query string) (Stmt, error) // Close会使当前准备好的语句和事物失效,并可能停止它们执行, // 将这个连接标记为不再使用 // // 因为sql包维护了一个空闲连接池,仅当前有多余的空闲连接时, // 才会调用Close。对于驱动来说,实现自己的连接缓存是不必要的 Close() error // Begin 启动并返回一个新的事务 // // 弃用:驱动应该通过实现ConnBeginTx来替换Begin Begin() (Tx, error) } // ConnPrepareContext通过使用context,加强了Conn接口 type ConnPrepareContext interface { // context被用来对语句做预处理 // 在语句本身中是不可以存储context的 PrepareContext(ctx context.Context, query string) (Stmt, error) } // IsolationLevel在TxOptions类型中记录事物的隔离级别 // // 这个类型应该认被为跟sql.IsolationLevel是一致的,以及定义这个类型的其他值。 // type IsolationLevel int // TxOptions 设置事物的选项 // // 这个类型应该被认为跟sql.TxOptions是一致的 type TxOptions struct { Isolation IsolationLevel ReadOnly bool } // ConnBeginTx通过context和TxOptions提高了Conn接口 type ConnBeginTx interface { // BeginTx启动并返回一个新的事务 // 如果context被用户取消了,sql包会在丢弃和关闭这个连接之前 // 执行Tx.Rollback // // 函数必须检查opts.Isolation,确定是否存在设置的额隔离级别。 // 如果驱动不支持一个非默认的隔离级别和被设置的级别,或者 // 存在一个非默认的隔离级别是不支持的,必须返回一个错误 // // 函数也必须检查opts.ReadOnly,如果ReadOnly值为真, // 如果支持设置的话,则设置只读事务属性。如果不支持的话,返回error // BeginTx(ctx context.Context, opts TxOptions) (Tx, error) } // Conn可能会实现SessionResetter接口,用于重置当前连接上的会话状态 // 并将当前连接标识为坏连接 type SessionResetter interface { // 当连接在连接池中时,调用ResetSession方法。该连接不会再承载任何查询操作 // 直接方法返回 // // 如果连接是坏的,方法应该返回driver.ErrBadConn错误,来阻止连接被放回到 // 连接池。其他别的错误将会被丢弃 ResetSession(ctx context.Context) error } // Result是查询执行的结果 type Result interface { // LastInsertId返回数据库自动生成的ID,比如,用主键插入表的操作 LastInsertId() (int64, error) // RowsAffected返回查询影响的行数 RowsAffected() (int64, error) } // Stmt是一个准备好的语句,它被绑定到一个Conn,且不能被多个goroutine并发 // 使用 type Stmt interface { // Close关闭这个语句 // // 截止到Go 1.1,如果Stmt在被一些查询使用,Stmt将不会被关闭 Close() error // NumInput返回占位符的个数 // // 如果NumInput返回值大于等于0,sql包会明智的检查调用者的参数个数, // 在Exec或Query被调用之前,返回错误给调用者。 // // 如果驱动不知道占位符的个数,NumInput可能会返回-1。在这种情况下, // sql包将不会检查Exec和Query的参数个数 NumInput() int // Exec执行一个不返回数据行的查询,比如INSERT或UPDATE // // 弃用:驱动应实现StmtExecContext来替代 Exec(args []Value) (Result, error) // Query执行一个返回数据行的查询,比如SELECT // // 弃用:驱动应实现StmtQueryContext来替代 Query(args []Value) (Rows, error) } // StmtExecContext升级了Stmt接口,它提供了一个有context的Exec, type StmtExecContext interface { // ExecContext执行一个不返回数据行的查询,比如INSERT或UPDATE // // ExecContext必须遵守context超时,当context被取消时,函数需要返回 ExecContext(ctx context.Context, args []NamedValue) (Result, error) } // StmtQueryContext升级了Stmt接口,它提供了一个有context的Query type StmtQueryContext interface { // QueryContext执行一个返回数据行的查询,比如SELECT // // ExecContext必须遵守context超时,当context被取消时,函数需要返回 QueryContext(ctx context.Context, args []NamedValue) (Rows, error) } // ErrRemoveArgument可能被NamedValueChecker返回,用来指示sql包不要 // 给驱动的query接口传递这个参数。 // 当接收到不是查询参数的特定属性或结构时,返回该错误 var ErrRemoveArgument = errors.New("driver: remove argument from query") // Conn或Stmt可选择是否要实现NamedValueChecker接口。接口提供给驱动 // 更多的控制,去处理超出Go和数据库允许的默认值类型。 // // 对于值的检查对象,sql包按如下顺序进行检查。当第一次发现匹配时停止: // Stmt.NamedValueChecker, Conn.NamedValueChecker, Stmt.ColumnConverter, // DefaultParameterConverter. // // 如果CheckNamedValue返回ErrRemoveArgument错误,那么这个NamedValue将不会 // 被包含在最终的查询参数中。这可能会被用于给查询传递特殊的选项。 // // 如果ErrSkip错误被返回,则使用列转换器错误检查路径作为参数。 // 驱动可能希望在耗尽自己特殊case后返回ErrSkip type NamedValueChecker interface { // 在传递参数给驱动之前,CheckNamedValue会被调用。 // 在任何ColumnConverter的地方也会调用CheckNamedValue。 // CheckNamedValue必须根据驱动的需要做类型校验和转换 CheckNamedValue(*NamedValue) error } // 如果语句知道自己列的类型,并且能够从任何类型转换为驱动的Value, // 那么Stmt 可以选择性的实现ColumnConverter // // 弃用:驱动应实现NamedValueChecker type ColumnConverter interface { // 根据提供的列序号,ColumnConverter返回一个 ValueConverter。 // 如果该列是未知的或者不需要被特殊处理,方法返回DefaultValueConverter ColumnConverter(idx int) ValueConverter } // Rows是一个查询结果的迭代器 type Rows interface { // Columns返回列的名字集,它的个数是从slice的长度中推断出来的。 // 如果不知道特定的列名,应该为该条目返回一个空的字符串 Columns() []string // 关闭行迭代器 Close() error // Next用于把数据集中的下一行填入提供的slice中。该slice的 // 长度应该跟Columns()的长度一致 // // 当没有数据行时,Next应该返回io.EOF // // dest不应被声明在Next之外(应该作为Next的一个成员变量) // 关闭Rows时应特别注意,不要修改dest中缓冲区的值 Next(dest []Value) error } // RowsNextResultSet扩展了Rows接口,它提供了一个方式,让驱动向前移动 // 到下一个结果集 type RowsNextResultSet interface { Rows // 在当前结果集的末尾调用HasNextResultSet,报告当前结果集之后是否还存在 // 别的结果集 HasNextResultSet() bool // NextResultSet向前移动驱动到下一个结果集,即使当前结果集 // 仍然存在剩余的数据行 // // 当不再有数据集时,NextResultSet应该返回io.EOF NextResultSet() error } // RowsColumnTypeScanType可以通过Rows实现,它应该返回可用于扫描的数据类型。 // 比如,数据库的列类型`bigint`应该返回"reflect.TypeOf(int64(0))". type RowsColumnTypeScanType interface { Rows ColumnTypeScanType(index int) reflect.Type } // RowsColumnTypeDatabaseTypeName可以通过Rows实现。它应该返回不包括字段长度的数据库类型, // 类型名应该全大写。诸如:"VARCHAR", "NVARCHAR", "VARCHAR2", "CHAR", "TEXT", // "DECIMAL", "SMALLINT", "INT", "BIGINT", "BOOL", "[]BIGINT", "JSONB", "XML", // "TIMESTAMP". type RowsColumnTypeDatabaseTypeName interface { Rows ColumnTypeDatabaseTypeName(index int) string } // RowsColumnTypeLength可以通过Rows实现,如果列是可变长度的类型,它应该返回 // 类型的长度。如果列是不可变长度的类型,ok返回false。 // 如果类型长度只受系统限制,则应该返回math.MaxInt64 // 下面是变量类型的返回值示例 // TEXT (math.MaxInt64, true) // varchar(10) (10, true) // nvarchar(10) (10, true) // decimal (0, false) // int (0, false) // bytea(30) (30, true) type RowsColumnTypeLength interface { Rows ColumnTypeLength(index int) (length int64, ok bool) } // RowsColumnTypeNullable可以通过Rows实现。如果指定的列可以为空,则nullable // 返回true。相反,如果列不能为空,nullable应该返回false。 // 如果不知道该列是否可以为空,ok应该返回false type RowsColumnTypeNullable interface { Rows ColumnTypeNullable(index int) (nullable, ok bool) } // RowsColumnTypePrecisionScale可以被Rows实现,对于decimal类型,它应该返回 // 精度和小数点右边的范围。如果类型不适用,ok应返回false // 下面是不同类型的返回值示例: // decimal(38, 4) (38, 4, true) // int (0, 0, false) // decimal (math.MaxInt64, math.MaxInt64, true) type RowsColumnTypePrecisionScale interface { Rows ColumnTypePrecisionScale(index int) (precision, scale int64, ok bool) } // Tx是一个事务 type Tx interface { Commit() error Rollback() error } // RowsAffected实现了INSERT或UPDATE操作的结果 // 表示被影响的行数 type RowsAffected int64 var _ Result = RowsAffected(0) func (RowsAffected) LastInsertId() (int64, error) { return 0, errors.New("LastInsertId is not supported by this driver") } func (v RowsAffected) RowsAffected() (int64, error) { return int64(v), nil } // ResultNoRows是一个预定义的结果,在一个DDL操作(比如CREATE TABLE)执行成功 // 时返回。调用该类型的LastInsertId和LastInsertId方法会返回错误 var ResultNoRows noRows type noRows struct{} var _ Result = noRows{} func (noRows) LastInsertId() (int64, error) { return 0, errors.New("no LastInsertId available after DDL statement") } func (noRows) RowsAffected() (int64, error) { return 0, errors.New("no RowsAffected available after DDL statement") }
终于介绍完啦!小伙伴们,这篇关于《Blog.1 database.sql.driver》的介绍应该让你收获多多了吧!欢迎大家收藏或分享给更多需要学习的朋友吧~golang学习网公众号也会发布数据库相关知识,快来关注吧!
声明:本文转载于:SegmentFault 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
-
499 收藏
-
244 收藏
-
235 收藏
-
157 收藏
-
101 收藏
最新阅读
更多>
-
214 收藏
-
155 收藏
-
485 收藏
-
436 收藏
-
125 收藏
-
174 收藏
课程推荐
更多>
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 507次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 484次学习
评论列表
-
- 阳光的心情
- 受益颇多,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,帮助很大,总算是懂了,感谢大佬分享技术文章!
- 2023-02-27 18:05:42
-
- 迷路的跳跳糖
- 太全面了,已加入收藏夹了,感谢作者大大的这篇技术文章,我会继续支持!
- 2023-02-12 09:57:35
-
- 无聊的大碗
- 写的不错,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,看完之后很有帮助,总算是懂了,感谢up主分享文章内容!
- 2023-02-04 17:42:50
-
- 花痴的香烟
- 这篇文章太及时了,细节满满,赞 👍👍,收藏了,关注up主了!希望up主能多写数据库相关的文章。
- 2023-02-04 16:14:46
-
- 火星上的含羞草
- 这篇文章太及时了,太详细了,真优秀,收藏了,关注师傅了!希望师傅能多写数据库相关的文章。
- 2023-01-31 12:38:31
-
- 成就的灰狼
- 这篇文章太及时了,太细致了,很好,mark,关注博主了!希望博主能多写数据库相关的文章。
- 2023-01-22 17:42:56
-
- 有魅力的飞机
- 太详细了,码起来,感谢楼主的这篇技术文章,我会继续支持!
- 2023-01-20 22:52:01
-
- 缥缈的小虾米
- 这篇技术文章真是及时雨啊,楼主加油!
- 2023-01-20 00:00:24
-
- 无限的老虎
- 这篇技术文章出现的刚刚好,很详细,很有用,码起来,关注楼主了!希望楼主能多写数据库相关的文章。
- 2023-01-14 23:14:10
-
- 强健的芹菜
- 这篇文章真及时,细节满满,很有用,收藏了,关注大佬了!希望大佬能多写数据库相关的文章。
- 2023-01-13 06:52:13
-
- 忧伤的路人
- 真优秀,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,看完之后很有帮助,总算是懂了,感谢楼主分享博文!
- 2023-01-12 04:00:44