技术好文:sqlx使用指南

简介: 技术好文:sqlx使用指南

这边文章主要基于Illustrated guide to SQLX翻译而成。


sqlx是一个go语言包,在内置database/sql包之上增加了很多扩展,简化数据库操作代码的书写。


资源


如果对于go语言的sql用法不熟悉,可以到下面网站学习:


database/sql documentation


go-database-sql tutorial


如果对于golang语言不熟悉,可以到下面网站学习:


The Go tour


How to write Go code


Effective Go


CSDN


由于database/sql接口是sqlx的子集,当前文档中所有关于database/sql的用法同样用于sqlx


开始


安装sqlx 驱动


?1$ go get github.com/jmoiron/sqlx


本文访问sqlite数据库


?1$ go get github.com/mattn/go-sqlite3


Handle Types


sqlx设计和database/sql使用方法是一样的。包含有4中主要的handle types:


- sqlx.DB - 和sql.DB相似,表示数据库。


- sqlx.Tx - 和sql.Tx相似,表示transacion。


- sqlx.Stmt - 和sql.Stmt相似,表示prepared statement。


- sqlx.NamedStmt - 表示prepared statement(支持named parameters)


所有的handler types都提供了对database/sql的兼容,意味着当你调用sqlx.DB.Query时,可以直接替换为sql.DB.Query.这就使得sqlx可以很容易的加入到已有的数据库项目中。


此外,sqlx还有两个cursor类型:


- sqlx.Rows - 和sql.Rows类似,Queryx返回。


- sqlx.Row - 和sql.Row类似,QueryRowx返回。


连级到数据库


一个DB实例并不是一个链接,但是抽象表示了一个数据库。这就是为什么创建一个DB时并不会返回错误和panic。它内部维护了一个连接池,当需要进行连接的时候尝试连接。你可以通过Open创建一个sqlx.DB或通过NewDb从已存在的sql.DB中创建一个新的sqlx.DB


?12345678910var db sqlx.DB // exactly the same as the built-indb = sqlx.Open("sqlite3", ":memory:") // from a pre-existing sql.DB; note the required driverNamedb = sqlx.NewDb(sql.Open("sqlite3", ":memory:"), "sqlite3") // force a connection and test that it workederr = db.Ping()


在一些环境下,你可能需要同时打开一个DB并链接。可以调用connect,这个函数打开一个新的DB并尝试Ping。MustConnect函数在链接出错时会panic。


?123456var err error// open and connect at the same time:db, err = sqlx.Connect("sqlite3", ":memory:") // open and connect at the same time, panicing on errordb = sqlx.MustConnect("sqlite3", ":memory:")


?123456789101112131415161718Querying 101sqlx中的handle types实现了数据库查询相同的基本的操作语法。- Exec(…) (sql.Result, error) - 和 database/sql相比没有改变- Query(…) (sql.Rows, error) - 和 database/sql相比没有改变- QueryRow(…) sql.Row - 和 database/sql相比没有改变 对内置语法的扩展- MustExec() sql.Result – Exec, but panic on error- Queryx(…) (sqlx.Rows, error) - Query, but return an sqlx.Rows- QueryRowx(…) sqlx.Row – QueryRow, but return an sqlx.Row 还有下面新的语法 Get(dest interface{}, …) errorSelect(dest interface{}, …) error下面会详细介绍这些方法的使用ExecExec和MustExec从连接池中获取一个连接然后只想对应的query操作。对于不支持ad-hoc query execution的驱动,在操作执行的背后会创建一个prepared statement。在结果返回前这个connection会返回到连接池中。


?1234567891011121314schema := CREATE TABLE place ( country text, city text NULL, telcode integer); // execute a query on the serverresult, err := db.Exec(schema) // or, you can use MustExec, which panics on errorcityState := INSERT INTO place (country, telcode) VALUES (?, ?)countryCity := INSERT INTO place (country, city, telcode) VALUES (?, ?, ?)db.MustExec(cityState, "Hong Kong", 852)db.MustExec(cityState, "Singapore", 65)db.MustExec(countryCity, "South Africa", "Johannesburg", 27)


//代码参考:https://weibo.com/u/7929311887

?123456789101112131415上面代码中 result有两个可能的数据LastInsertId() or RowsAffected(),依赖不同的驱动。Mysql中,在含有auto-increment key的表中执行插入操作会得到LastInsertId(),在PostgreSQL中这个信息只有在使用RETURNING语句的row cursor中才会返回。 bindvars代码中?占位符,称为bindvars,非常重要,你可以总是使用它们来向数据库发送数据,可以用来组织SQL Injection攻击。database/sql并不会对查询语句进行任何的校验,传入什么就发送到server是什么。除非driver实现特定的接口,query在数据库执行之前会准备好。不同的数据库的bindvars不一样。- MySQL 使用?- PostgreSQL 使用1,1,2等等- SQLite 使用?或$1- Oracle 使用:name 其他数据库可能还不一样。你可以使用sqlx.DB.Rebind(string) string函数利用?语法来得到一个适合在当前数据库上执行的query语句。 关于bindvars常见的误解是他们用于插值。他们只用于参数化,不允许改变sql语句的合法接口。例如,下面的用法是会报错的


?12345// doesn't workdb.Query("SELECT FROM ?", "mytable") // also doesn't workdb.Query("SELECT ?, ? FROM people", "name", "location")


Query


Query是database/sql中执行查询主要使用的方法,该方法返回row结果。Query返回一个sql.Rows对象和一个error对象。


?1234567891011// fetch all places from the dbrows, err := db.Query("SELECT country, city, telcode FROM place") // iterate over each rowfor rows.Next() { var country string // note that city can be NULL, so we use the NullString type var city sql.NullString var telcode int err = rows.Scan(&country, &city, &telcode)}


在使用的时候应该吧Rows当成一个游标而不是一系列的结果。尽管数据库驱动缓存的方法不一样,通过Next()迭代每次获取一列结果,对于查询结果非常巨大的情况下,可以有效的限制内存的使用,Scan()利用reflect把sql每一列结果映射到go语言的数据类型如string,【】byte等。如果你没有遍历完全部的rows结果,一定要记得在把connection返回到连接池之前调用rows.Close()。


Query返回的error有可能是在server准备查询的时候发生的,也有可能是在执行查询语句的时候发生的。例如可能从连接池中获取一个坏的连级(尽管数据库会尝试10次去发现或创建一个工作连接)。一般来说,错误主要由错误的sql语句,错误的类似匹配,错误的域名或表名等。


在大部分情况下,Rows.Scan()会把从驱动获取的数据进行拷贝,无论驱动如何使用缓存。特殊类型sql.RawBytes可以用来从驱动返回的数据总获取一个zero-copy的slice byte。当下一次调用Next的时候,这个值就不在有效了,因为它指向的内存已经被驱动重写了别的数据。


Query使用的connection在所有的rows通过Next()遍历完后或者调用rows.Close()后释放。


Queryx和Query行为很相似,不过返回一个sqlx.Rows对象,支持扩展的scan行为。


?1234567891011type Place struct { Country string City sql.NullString TelephoneCode int db:"telcode"} rows, err := db.Queryx("SELECT FROM place")for rows.Next() { var p Place err = rows.StructScan(&p)}


sqlx.Rowx的主要扩展就是StructScan,可以自动把查下结果扫描到对应结构体中的域(fileld)中。注意结构体中域(field)必须是可导出(exported)的,这样sqlx才能够写入值到结构体中。


正如在上面代码中所示,可以利用db结构体标签来指定结构体field映射到数据库中特定的列名,或者用db.MapperFunc()来指定默认的映射。db默认对结构体的filed名执行strings.Lower后,和数据库的列名进行匹配。关于StructScan,SliceScan,MapScan更详细的内容请参见后面章节advanced scanning


QueryRow


QueryRow从数据库server中获取一列数据。它从连接池中获取一个连级,然后执行Query,返回一个Row对象,这个对象有一个自己的内部的Rows对象。


?123row := db.QueryRow("SELECT FROM place WHERE telcode=?", 852)var telcode interr = row.Scan(&telcode)


不像Query,QueryRow只返回一个Row类型,并不返回error,如果在执行查询过程中出错,则错误通过Scan返回,如果查询结果为空,则返回sql.ErrNoRows。如果Scan本身出错,error同样由scan返回。


QueryRow使用的connection当result返回的时候就关闭了,也就意味着使用QueryRow的时候不能够使用sql.RawByes,因为driver使用sql.RawBytes引用内存,在connection回收后可能也会无效。


QueryRowx返回一个sqlx.Row而不是sql.Row,它实现了跟Rows相同的scan方法如上,同时还有高级的scan方法如下:(更高级的scan方法advanced scanning section)


var p Place


err := db.QueryRowx("SELECT city, telcode FROM place LIMIT 1").StructScan(&p)


Get and Select


Get和Select是一个非常省时的扩展。它们把query和非常灵活的scan语法结合起来。为了更加清晰的介绍它们,我们先讨论下什么是scannalbe:


a value is scannable if it is not a struct, eg string, int


a value is scannable if it implements sql.Scanner


a value is scannable if it is a struct with no exported fields (eg. time.Time)


Get和Select对scannable的类型使用rows.scan,对non-scannable的类型使用rows.StructScan。Get用来获取单个结果然后Scan,Select用来获取结果切片。


?12345678910111213141516p := Place{}pp := 【】Place{} // this will pull the first place directly into perr = db.Get(&p, "SELECT FROM place LIMIT 1") // this will pull places with telcode > 50 into the slice pperr = db.Select(&pp, "SELECT FROM place WHERE telcode > ?", 50) // they work with regular types as wellvar id interr = db.Get(&id, "SELECT count(*) FROM place") // fetch at most 10 place namesvar names 【】stringerr = db.Select(&names, "SELECT name FROM place LIMIT 10")


Get和Select在执行完查询后就会关闭Rows,并且在执行阶段遇到任何问题都会返回错误。由于它们内部使用的StructScan,所以 下文中advanced scanning section讲的特征也适用与Get和Select。


Select可以提高编码小路,但是要注意Select和Queryx是有很大不同的,因为Select会把整个结果一次放入内存。如果查询结果没有限制特定的大小,那么最好使用Query/StructScan迭代方法。


Transactions


为了使用transactions,必须使用DB.Begin()来创建,下面的代码是错误的:


?123db.MustExec("BEGIN;")db.MustExec(...)db.MustExec("COMMIT;")


Exec和其他查询语句会向DB请求一个connection,执行完后就返回到连接池中,并不能保证每次获取的connection就是BEGIN执行时使用的那个,所以正确的做法要使用DB.Begin:


?

相关文章
|
7月前
|
存储 测试技术 iOS开发
CocoaLumberjack的ios应用开发使用指南
CocoaLumberjack的ios应用开发使用指南
200 2
|
运维 数据库 对象存储
云运维工程师必读系列电子书全览【持续更新】
云运维工程师不可错过的匠心之作,一次下载,长期受用!
云运维工程师必读系列电子书全览【持续更新】
|
人工智能 移动开发 大数据
程序人生 - 【官方指南】教你如何快速成为CSDN博客专家!
程序人生 - 【官方指南】教你如何快速成为CSDN博客专家!
210 0
程序人生 - 【官方指南】教你如何快速成为CSDN博客专家!
|
机器学习/深度学习
R爱好者福利|免费线上资源分享
最近有些R爱好者想要寻求R语言相关资料,以下为小编平常经常使用的免费开源的R语言资料,和大家分享。 其他统计,R语言书籍小编这也搜集了很多,但是由于版权等问题,不能直接分享,需要可后台联系,或者加小编微信(菜单:资料获取)。
278 0
R爱好者福利|免费线上资源分享
|
弹性计算 分布式计算 NoSQL
开发者社区精选直播合集(三十四)| 数据迁移工具与实践
本文档围绕如何将您的数据迁移到阿里云,提供了多个场景的实践方案及工具
开发者社区精选直播合集(三十四)| 数据迁移工具与实践
|
人工智能 前端开发 物联网
平头哥智能语音最佳应用实践 | 开发者社区精选文章合集(二十八)
随着社会未来向智能化的逐渐发展,数字化的趋势也愈加确定无疑的。智能语音技术的发展,优化了产品的便捷体验,让越来越多的企业和终端用户看到了“智能生活”的增长空间。
平头哥智能语音最佳应用实践 | 开发者社区精选文章合集(二十八)
|
人工智能 Kubernetes 小程序
开发者社区精选直播合集(二十七)| HaaS物联网最佳实践
HaaS(Hardware as a Service)物联网设备云端一体开发框架,整合阿里云、达摩院、平头哥技术,基于数亿物联网设备接入经验,提供积木式硬件开发能力,实现低代码快速开发,帮助中小开发者聚焦业务,实现设备安全上云,加速设备创新迭代。
开发者社区精选直播合集(二十七)| HaaS物联网最佳实践
|
存储 机器学习/深度学习 弹性计算
开发者社区精选直播合集(二十五)| 企业上云宝典
企业上云有利于更好地促进各类信息技术在企业中的普及应用,从软件,平台,网络等方面加快两化深度融合的步伐,迅速便捷高效地提高生产管理效率,优化业务流程,加速培育新产品,新模式,实现产业链上下的高效对接和协同创新,重塑生产组织方式和创新机制,是目前企业发展的必然趋势。
开发者社区精选直播合集(二十五)| 企业上云宝典
|
人工智能 Kubernetes 小程序
开发者社区精选直播合集(二十一)| 云上快速搭建小程序
本合集由浅入深,旨在帮助小程序开发者们解决开发中遇到的问题,为开发者们快速赋能,提高开发体验。
开发者社区精选直播合集(二十一)|  云上快速搭建小程序
|
人工智能 Kubernetes 小程序
开发者社区精选直播合集(十六)| 云开发平台小课合集
积跬步以至千里,云开发小课跟你一起脚踏实地学习云原生开发新范式
开发者社区精选直播合集(十六)| 云开发平台小课合集
下一篇
DataWorks