在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
1.1 Golang中的Mysql驱动A. https://github.com/go-sql-driver/mysql B. Go本身不提供具体数据库驱动,只提供驱动接口和管理。(官方只是封装了接口并没有提供具体的实现) C. 各个数据库驱动需要第三方实现,并且注册到Go中的驱动管理中。 1.2 Mysql驱动,注册示例
解释: 其实就是把mysql驱动注册到golang官方的接口中,然后在通过golang的接口去操作mysql具体实例了。驱动的名字叫mysql。 1.3 导入Mysql驱动import ( "database/sql" _ "github.com/go-sql-driver/mysql" ) 解释: 可能会好奇为什么导入"database/sql"后,为什么导入mysql驱动却要加"_"来忽略掉呢?其实真正的原因是因为init函数的原因,下面通过一个实例来了解一下: 写一个自定义的包: 目录结构如下: custom_pkg: custom.go: package custom_pkg import ( "fmt" ) var Count int func init() { fmt.Printf("hello world, i'm in init function\n") } custom2.go: package custom_pkg import ( "fmt" ) func init() { fmt.Printf("hello world, i'm in init function custom2\n") } custom_example: main.go: package main import ( "6/After_class/mysql/custom_pkg" "fmt" ) func main() { fmt.Printf("count=%d\n", custom_pkg.Count) } 执行结果如下: 可以发现执行的结果是将导入的包的init函数执行了。 总结:葵花宝典 init函数名字是固定的,没有参数也没有返回值,其是包用来做初始化的,会自动执行(在包导入的时候执行),在一个包中这个函数可以有多个。所以之后我们就可以利用init函数做一些包初始化的逻辑。 了解:init函数执行顺序,先执行导入的包的init函数,在执行当前包的init函数。 所以说在倒入mysql驱动时为什么要加"_"来忽略掉呢?就是因为想要执行init函数。 1.4 连接数据库先安装mysql驱动: go get -u github.com/go-sql-driver/mysql 连接实例如下: package main import ( "database/sql" "fmt" _ "github.com/go-sql-driver/mysql" ) func main() { dsn := "root:123456@tcp(127.0.0.1:3306)/golang" //连接数据库的配置 Db, err := sql.Open("mysql", dsn) //Open函数第一个参数就是驱动的名字(不能随意写的) if err != nil { //有一个坑,如果连接数据库配置写错了,不会在此处报错 fmt.Printf("open mysql is failed,err:%v\n", err) return } err = Db.Ping() //ping一下没报错证明连接数据库成功 if err != nil { fmt.Printf("ping is failed,err:%v\n", err) return } fmt.Printf("connect to db successful\n") } 执行结果: 二、Mysql数据基本操作首先准备环境先创建一张表 建表语句如下: 2.1 sql查询A. 单行查询, Db.QueryRow B 多行查询, Db.Query
实例1:单行查询 Db.QueryRow package main import ( "database/sql" "fmt" _ "github.com/go-sql-driver/mysql" ) type User struct { //为要查询的表定义一个结构体(数据库定义的表要有1个结构体来封装,否则会十分混乱) Id int64 `db:"id"` //数据库一般都是小写,所以我们需要借助tag来映射(结构体中字段名写什么就无所谓了,反正要做映射) Name string `db:"name"` Age int `db:"age"` } func QueryRow(Db *sql.DB) { id := 1 row := Db.QueryRow("select id,name,age from user where id=?", id) //?是占位符,可以传入数值拼接成完整的sql语句发给mysql服务器,然后mysql返回数据集。 var user User err := row.Scan(&user.Id, &user.Name, &user.Age) //Scan这里查询有2种错误 1、查询不到数据失败(网络原因等);2、查询过程中查询失败(和因为网络原因造成的错误区分开) if err == sql.ErrNoRows { //对应第二种错误 ErrNoRows表示查询过程中是没发生错误的 fmt.Printf("not found data of id:%d\n", id) return } if err != nil { //对应第一种错误 fmt.Printf("scan failed, err:%v\n", err) return } fmt.Printf("user:%#v\n", user) } func main() { dsn := "root:123456@tcp(127.0.0.1:3306)/golang" Db, err := sql.Open("mysql", dsn) if err != nil { fmt.Printf("open mysql is failed,err:%v\n", err) return } err = Db.Ping() if err != nil { fmt.Printf("ping is failed,err:%v\n", err) return } fmt.Printf("connect to db successful\n") QueryRow(Db) } 执行结果:
实例2:多行查询, Db.Query package main import ( "database/sql" "fmt" _ "github.com/go-sql-driver/mysql" ) type User struct { Id int64 `db:"id"` Name string `db:"name"` Age int `db:"age"` } func Query(Db *sql.DB) { id := 0 rows, err := Db.Query("select id, name, age from user where id>?", id) //rows返回的是1个结果集,这个rows结果集,用完之后,一定释放!rows.Close() 这个是消耗数据库连接资源的。 defer func() { if rows != nil { rows.Close() } }() if err == sql.ErrNoRows { fmt.Printf("not found data of id:%d\n", id) return } if err != nil { fmt.Printf("scan failed, err:%v\n", err) return } for rows.Next() { //多行数据通过写一个for循环来进行处理,有数据返回true接着执行,没数据返回false就退出 Next就如同一个游标,查到最后就会返回一个false,就查询结束退出循环了。 var user User err := rows.Scan(&user.Id, &user.Name, &user.Age) if err != nil { fmt.Printf("scan failed, err:%v\n", err) return } fmt.Printf("user:%#v\n", user) } } func main() { dsn := "root:123456@tcp(127.0.0.1:3306)/golang" Db, err := sql.Open("mysql", dsn) if err != nil { fmt.Printf("open mysql is failed,err:%v\n", err) return } err = Db.Ping() if err != nil { fmt.Printf("ping is failed,err:%v\n", err) return } fmt.Printf("connect to db successful\n") Query(Db) } 执行结果如下: 2.2 Mysql 插入、更新和删除A. Db.Exec(query, args…interface{})(Result, error) 解释:插入、更新、删除都是Exec方法(sql语句,传入占位符对应的值)(返回结果集,错误) B. 插入的主键id获取, Result. LastInsertId()
插入实例: package main import ( "database/sql" "fmt" _ "github.com/go-sql-driver/mysql" ) type User struct { Id int64 `db:"id"` Name string `db:"name"` Age int `db:"age"` } func Insert(DB *sql.DB) { username := "user01" age := 18 result, err := DB.Exec("insert into user(name, age)values(?, ?)", username, age) if err != nil { fmt.Printf("exec failed, err:%v\n", err) return } id, err := result.LastInsertId() //获取最后一次插入的id if err != nil { fmt.Printf("last insert id failed, err:%v\n", err) return } affectRows, err := result.RowsAffected() //此次操作影响的行数 if err != nil { fmt.Printf("affectRows failed, err:%v\n", err) return } fmt.Printf("last insert id:%d affect rows:%d\n", id, affectRows) } func main() { dsn := "root:123456@tcp(127.0.0.1:3306)/golang" Db, err := sql.Open("mysql", dsn) if err != nil { fmt.Printf("open mysql is failed,err:%v\n", err) return } err = Db.Ping() if err != nil { fmt.Printf("ping is failed,err:%v\n", err) return } fmt.Printf("connect to db successful\n") Insert(Db) } 执行结果:
更新实例: package main import ( "database/sql" "fmt" _ "github.com/go-sql-driver/mysql" ) type User struct { Id int64 `db:"id"` Name string `db:"name"` Age int `db:"age"` } func Update(DB *sql.DB) { username := "user02" age := 108 id := 3 result, err := DB.Exec("update user set name=?, age=? where id=?", username, age, id) if err != nil { fmt.Printf("exec failed, err:%v\n", err) return } affectRows, err := result.RowsAffected() if err != nil { fmt.Printf("affectRows failed, err:%v\n", err) return } fmt.Printf("last update id:%d affect rows:%d\n", id, affectRows) } func main() { dsn := "root:123456@tcp(127.0.0.1:3306)/golang" Db, err := sql.Open("mysql", dsn) if err != nil { fmt.Printf("open mysql is failed,err:%v\n", err) return } err = Db.Ping() if err != nil { fmt.Printf("ping is failed,err:%v\n", err) return } fmt.Printf("connect to db successful\n") Update(Db) } 执行结果:
数据库结果:
删除实例: package main import ( "database/sql" "fmt" _ "github.com/go-sql-driver/mysql" ) type User struct { Id int64 `db:"id"` Name string `db:"name"` Age int `db:"age"` } func Delete(DB *sql.DB) { id := 3 result, err := DB.Exec("delete from user where id=?", id) if err != nil { fmt.Printf("exec failed, err:%v\n", err) return } affectRows, err := result.RowsAffected() if err != nil { fmt.Printf("affectRows failed, err:%v\n", err) return } fmt.Printf("last delete id:%d affect rows:%d\n", id, affectRows) } func main() { dsn := "root:123456@tcp(127.0.0.1:3306)/golang" Db, err := sql.Open("mysql", dsn) if err != nil { fmt.Printf("open mysql is failed,err:%v\n", err) return } err = Db.Ping() if err != nil { fmt.Printf("ping is failed,err:%v\n", err) return } fmt.Printf("connect to db successful\n") Delete(Db) } 执行结果如下:
数据库结果: 三、Mysql预处理3.1 一般sql处理流程1. 客户端拼接好sql语句 2. 客户端发送sql语句到mysql服务器 3. mysql服务器解析sql语句并执行, 把执行结果发送给客户端 3.2 预处理处理流程1. 把sql分为两部分,命令部分和数据部分。 2. 首先把命令部分发送给mysql服务器, mysql进行sql预处理 3. 然后把数据部分发送给mysql服务器, mysql进行占位符替换 4. mysql服务器执行sql语句并返回结果给客户端 3.3 Mysql预处理优势A. 同一条sql反复执行,性能会很高。 B. 避免sql注入问题 3.4 Mysql预处理实例A. 查询操作, 1. Db.Prepare(sql string) (*sql.Stmt, error) stmt可以暂且理解为语句 2. Stmt.Query() B. 更新操作(插入、更新、 delete), 1. Db.Prepare(sql string) (*sql.Stmt, error) 2. Stmt.Exec()
查询操作预处理实例: package main import ( "database/sql" "fmt" _ "github.com/go-sql-driver/mysql" ) type User struct { Id int64 `db:"id"` Name string `db:"name"` Age int `db:"age"` } func PrepareQuery(DB *sql.DB) { //预处理第1步:发送sql语句命令部分 stmt, err := DB.Prepare("select id, name, age from user where id>?") if err != nil { fmt.Printf("prepare failed, err:%v\n", err) return } //预处理第2步:发送sql语句数据部分 id := 1 rows, err := stmt.Query(id) //这个rows结果集,用完之后,一定释放!rows.Close() defer func() { if rows != nil { rows.Close() } if stmt != nil { //stmt记住也要释放 stmt.Close() } }() if err == sql.ErrNoRows { fmt.Printf("not found data of id:%d\n", id) return } if err != nil { fmt.Printf("scan failed, err:%v\n", err) return } for rows.Next() { var user User err := rows.Scan(&user.Id, &user.Name, &user.Age) if err != nil { fmt.Printf("scan failed, err:%v\n", err) return } fmt.Printf("user:%#v\n", user) } } func main() { dsn := "root:123456@tcp(127.0.0.1:3306)/golang" Db, err := sql.Open("mysql", dsn) if err != nil { fmt.Printf("open mysql is failed,err:%v\n", err) return } err = Db.Ping() if err != nil { fmt.Printf("ping is failed,err:%v\n", err) return } fmt.Printf("connect to db successful\n") PrepareQuery(Db) } 执行结果如下:
更新操作预处理实例:(这里以insert举例) package main import ( "database/sql" "fmt" _ "github.com/go-sql-driver/mysql" ) type User struct { Id int64 `db:"id"` Name string `db:"name"` Age int `db:"age"` } func PrepareInsert(DB *sql.DB) { stmt, err := DB.Prepare("insert into user(name, age)values(?, ?)") if err != nil { fmt.Printf("prepare failed, err:%v\n", err) return } result, err := stmt.Exec("user10111", 108) if err != nil { fmt.Printf("exec failed, err:%v\n", err) return } id, err := result.LastInsertId() if err != nil { fmt.Printf("last insert id failed, err:%v\n", err) return } affectRows, err := result.RowsAffected() if err != nil { fmt.Printf("affectRows failed, err:%v\n", err) return } fmt.Printf("last insert id:%d affect rows:%d\n", id, affectRows) } func main() { dsn := "root:123456@tcp(127.0.0.1:3306)/golang" Db, err := sql.Open("mysql", dsn) if err != nil { fmt.Printf("open mysql is failed,err:%v\n", err) return } err = Db.Ping() if err != nil { fmt.Printf("ping is failed,err:%v\n", err) return } fmt.Printf("connect to db successful\n") PrepareInsert(Db) } 执行结果: 数据库结果: 四、Mysql事务4.1 应用场景1. 同时更新,多个表。 2. 同时更新多行数据。 4.2 事务的ACID1. 原子性 2. 一致性 3. 隔离性 4. 持久性 4.3 Mysql的事务实例A. Db.Begin(),开启一个事务 B. Db.Commit(),提交一个事务 C. Db.Rollback(),回滚一个事务
关于事务的实例如下: package main import ( "database/sql" "fmt" _ "github.com/go-sql-driver/mysql" ) type User struct { Id int64 `db:"id"` Name string `db:"name"` Age int `db:"age"` } func Transaction(Db *sql.DB) { //开启事务 tx, err := Db.Begin() //tx就是事务这次用来操作数据库的1个连接 if err != nil { fmt.Printf("begin failed, err:%v\n", err) return } _, err = tx.Exec("insert into user(name, age)values(?, ?)", "user0101", 108) //插入一条数据 if err != nil { tx.Rollback() return } _, err = tx.Exec("update user set name=?, age=?", "user0101", 108) if err != nil { tx.Rollback() //如果有错误,则回滚 return } err = tx.Commit() //提交事务 if err != nil { tx.Rollback() return } } func main() { dsn := "root:123456@tcp(127.0.0.1:3306)/golang" Db, err := sql.Open("mysql", dsn) if err != nil { fmt.Printf("open mysql is failed,err:%v\n", err) return } err = Db.Ping() if err != nil { fmt.Printf("ping is failed,err:%v\n", err) return } fmt.Printf("connect to db successful\n") Transaction(Db) } 执行结果如下:
数据库结果:
五、sqlx库介绍与使用5.1 介绍
sqlx是在数据库层面的驱动进行封装的。主要做的是对查询的优化 github地址: https://github.com/jmoiron/sqlx 安装: go get github.com/jmoiron/sqlx
5.2 好处A. 使用更简单 B. 支持多数据库, mysql、 postgresql、 oracle、 sqlite 5.3 开发与使用A. 查询, sqlx.DB.Get(查单行)和sqlx.DB.Select(查多行) B. 更新、插入和删除, sqlx.DB.Exec C. 事务, sqlx.DB.Begin()、 sqlx.DB.Commit、 sqlx.DB.rollback 5.3.1 查询查询这里对go语言原生sql方法做了优化,封装了接口,所以用sqlx做查询较为方便,不需要像原生一样去考虑结果集close问题、for循环遍历问题、append问题,sqlx帮我们把上述问题都做了(其底层借助反射机制封装了接口)。 单行查询:sqlx.DB.Get 实例: package main import ( "database/sql" "fmt" _ "github.com/go-sql-driver/mysql" "github.com/jmoiron/sqlx" ) type User struct { Id int64 `db:"id"` Name string `db:"name"` Age int `db:"age"` } func QueryRow(Db *sqlx.DB) { //查询单行数据 id := 2 var user User err := Db.Get(&user, "select id, name, age from user where id=?", id) if err == sql.ErrNoRows { //无数据报错 fmt.Printf("not record found\n") return } if err != nil { //查询失败报错 fmt.Printf("get failed, err:%v\n", err) return } fmt.Printf("get user succ, user:%#v\n", user) } func main() { dns := "root:123456@tcp(127.0.0.1:3306)/golang" Db, err := sqlx.Connect("mysql", dns) //sqlx.connect连接数据库 if err != nil { fmt.Printf("open mysql failed, err:%v\n", err) return } err = Db.Ping() if err != nil { fmt.Printf("ping failed, err:%v\n", err) return } fmt.Printf("connect to db succ\n") QueryRow(Db) } 执行结果:
多行查询:sqlx.DB.Select 实例: package main import ( "fmt" _ "github.com/go-sql-driver/mysql" "github.com/jmoiron/sqlx" ) type User struct { Id int64 `db:"id"` Name string `db:"name"` Age int `db:"age"` } func Query(Db *sqlx.DB) { var user []*User //多行数据存 |
请发表评论