sqlx操作MySQL实战及其原理

sqlx是Golang中的一个知名三方库,其为Go标准库database/sql提供了一组扩展支持 。使用它可以方便的在数据行与Golang的结构体、映射和切片之间进行转换,从这个角度可以说它是一个ORM框架;它还封装了一系列地常用SQL操作方法,让我们用起来更爽 。
sqlx实战这里以操作MySQL的增删改查为例 。
准备工作【sqlx操作MySQL实战及其原理】先要准备一个MySQL,这里通过Docker快速启动一个MySQL 5.7 。
docker run -d --name mysql1 -p 3306:3306 -e MYSQL_ROOT_PASSword=123456 mysql:5.7在MySQL中创建一个名为test的数据库:
CREATE DATABASE `test` /*!40100 DEFAULT CHARACTER SET utf8mb4 */;数据库中创建一个名为Person的数据库表:
CREATE TABLE test.Person (Id integer auto_increment NOT NULL,Name VARCHAR(30) NULL,City VARCHAR(50) NULL,AddTime DATETIME NOT NULL,UpdateTime DATETIME NOT NULL,CONSTRAINT Person_PK PRIMARY KEY (Id))ENGINE=InnoDBDEFAULT CHARSET=utf8mb4COLLATE=utf8mb4_general_ci;然后创建一个Go项目,安装sqlx:
go get github.com/jmoiron/sqlx因为操作的是MySQL,还需要安装MySQL的驱动:
go get github.com/go-sql-driver/mysql编写代码添加引用添加sqlx和mysql驱动的引用:
import ("log"_ "github.com/go-sql-driver/mysql""github.com/jmoiron/sqlx")MySQL的驱动是隐式注册的,并不会在接下来的程序中直接调用,所以这里加了下划线 。
创建连接操作数据库前需要先创建一个连接:
db, err := sqlx.Connect("mysql", "root:123456@tcp(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=true&loc=Local")if err != nil {log.Println("数据库连接失败")}这个连接中指定了程序要用MySQL驱动,以及MySQL的连接地址、用户名和密码、数据库名称、字符编码方式;这里还有两个参数parseTime和loc,parseTime的作用是让MySQL中时间类型的值可以映射到Golang中的time.Time类型,loc的作用是设置time.Time的值的时区为当前系统时区,不使用这个参数的话保存到的数据库的就是UTC时间,会和北京时间差8个小时 。
增删改查sqlx扩展了DB和Tx,继承了它们原有的方法,并扩展了一些方法,这里主要看下这些扩展的方法 。
增加
通用占位符的方式:
insertResult := db.MustExec("INSERT INTO Person (Name, City, AddTime, UpdateTime) VALUES (?, ?, ?, ?)", "Zhang San", "Beijing", time.Now(), time.Now())lastInsertId, _ := insertResult.LastInsertId()log.Println("Insert Id is ", lastInsertId)这个表的主键使用了自增的方式,可以通过返回值的LastInsertId方法获取 。
命名参数的方式:
insertPerson := &Person{Name:"Li Si",City:"Shanghai",AddTime:time.Now(),UpdateTime: time.Now(),}insertPersonResult, err := db.NamedExec("INSERT INTO Person (Name, City, AddTime, UpdateTime) VALUES(:Name, :City, :AddTime, :UpdateTime)", insertPerson)命名参数的方式是sqlx扩展的,这个方式就是常说的ORM 。这里需要注意给struct字段添加上db标签:
type Person struct {Idint`db:"Id"`Namestring`db:"Name"`Citystring`db:"City"`AddTimetime.Time `db:"AddTime"`UpdateTime time.Time `db:"UpdateTime"`}struct中的字段名称不必和数据库字段相同,只需要通过db标签映射正确就行 。注意SQL语句中使用的命名参数需要是db标签中的名字 。
除了可以映射struct,sqlx还支持map,请看下面这个示例:
insertMap := map[string]interface{}{"n": "Wang Wu","c": "HongKong","a": time.Now(),"u": time.Now(),}insertMapResult, err := db.NamedExec("INSERT INTO Person (Name, City, AddTime, UpdateTime) VALUES(:n, :c, :a, :u)", insertMap)再来看看批量增加的方式:
insertPersonArray := []Person{{Name: "BOSIMA", City: "Wu Han", AddTime: time.Now(), UpdateTime: time.Now()},{Name: "BOSSMA", City: "Xi An", AddTime: time.Now(), UpdateTime: time.Now()},{Name: "BOMA", City: "Cheng Du", AddTime: time.Now(), UpdateTime: time.Now()},}insertPersonArrayResult, err := db.NamedExec("INSERT INTO Person (Name, City, AddTime, UpdateTime) VALUES(:Name, :City, :AddTime, :UpdateTime)", insertPersonArray)if err != nil {log.Println(err)return}insertPersonArrayId, _ := insertPersonArrayResult.LastInsertId()log.Println("InsertPersonArray Id is ", insertPersonArrayId)这里还是采用命名参数的方式,参数传递一个struct数组或者切片就可以了 。这个执行结果中也可以获取到最后插入数据的自增Id,不过实测返回的是本次插入的第一条的Id,这个有点别扭,但是考虑到增加多条只获取一个Id的场景似乎没有,所以也不用多虑 。


推荐阅读