返回

Go 的 MySQL 预处理、MySQL 事务

发布时间:2022-12-03 08:57:45 351
# php# 数据库# 服务器# github# 数据

Go 的 MySQL 预处理、MySQL 事务_sql

预处理是什么

Go 的 MySQL 预处理、MySQL 事务_sql_02


在普通 SQL 语句执行过程中,客户端会对 SQL 语句进行占位符替换,从而得到要执行的完整 SQL 语句,客户端再将此 SQL 语句发送到服务端执行,服务端最后把结果返回给客户端。

而预处理,则是将 SQL 语句分为命令部分以及数据部分,客户端先把命令部分发送给服务器,服务器先进行预处理,而后客户端才把数据部分发送给服务器,由服务器对 SQL 语句进行占位符替换并执行,最后将结果返回给客户端。

预处理可以提高服务器的性能,提前让服务器编译,一次编译多次执行,甚至可以避免 SQL 注入问题。


Go 的 MySQL 预处理、MySQL 事务_sql

Go 实现 MySQL 预处理

Go 的 MySQL 预处理、MySQL 事务_sql_02


在上一期​​《Go 操作 MySQL 数据库》​​​中,很多例子都使用了预处理。Go 中的 ​​Prepare()​​ 方法会将 SQL 语句发送给服务器,返回一个准备好的状态用于之后的查询和命令,返回值可以同时执行多个查询和命令:

func (db *DB) Prepare(query string) (*Stmt, error) {
return db.PrepareContext(context.Background(), query)
}

下面是一个查询操作预处理的例子:

package main

import (
"database/sql"
"fmt"

_ "github.com/go-sql-driver/mysql"
)

type User struct {
id int
phone string
nickName string
age int
}

func main() {
// 连接数据库
db, _ := sql.Open("mysql", "root:root@(127.0.0.1:3306)/godb_test?charset=utf8mb4")

// Ping() 验证与数据库的连接是否仍处于活动状态,并在必要时建立连接
err := db.Ping()

if err != nil {
fmt.Println("Database connection failed")
return
}

// 延迟调用关闭数据库 阻止新的查询
defer db.Close()

// 准备 SQL 语句
sqlStr := "select id, nick_name, phone, age from acl_user where id > ? and is_deleted = 0"

stmt, err := db.Prepare(sqlStr)
if err != nil {
fmt.Println("Prepare failed")
return
}

defer stmt.Close()
rows, err := stmt.Query("100")

if err != nil {
fmt.Println("Query failed")
return
}

defer rows.Close()

// 读取结果集中的数据
for rows.Next() {
var user User
err := rows.Scan(&user.id, &user.nickName, &user.phone, &user.age)
if err != nil {
fmt.Println("Scan failed")
return
}
fmt.Println(user)
}

}

Go 的 MySQL 预处理、MySQL 事务_sql

Go 实现 MySQL 事务

Go 的 MySQL 预处理、MySQL 事务_sql_02


Go 语言中使用下面的方法实现 MySQL 事务操作:

// 开启事务方法
func (db *DB) Begin() (*Tx, error) {
return db.BeginTx(context.Background(), nil)
}

// 回滚事务方法
func (tx *Tx) Rollback() error {
return tx.rollback(false)
}

// 提交事务方法
func (tx *Tx) Commit() error {
select {
default:
case <-tx.ctx.Done():
if atomic.LoadInt32(&tx.done) == 1 {
return ErrTxDone
}
return tx.ctx.Err()
}
if !atomic.CompareAndSwapInt32(&tx.done, 0, 1) {
return ErrTxDone
}

tx.cancel()
tx.closemu.Lock()
tx.closemu.Unlock()

var err error
withLock(tx.dc, func() {
err = tx.txi.Commit()
})
if err != driver.ErrBadConn {
tx.closePrepared()
}
tx.close(err)
return err
}

下面是一个事务操作的例子,该事务操作确保两次更新操作要么同时成功要么同时失败:

package main

import (
"database/sql"
"fmt"

_ "github.com/go-sql-driver/mysql"
)

func main() {
// 连接数据库
db, _ := sql.Open("mysql", "root:root@(127.0.0.1:3306)/godb_test?charset=utf8mb4")

// Ping() 验证与数据库的连接是否仍处于活动状态,并在必要时建立连接
err := db.Ping()

if err != nil {
fmt.Println("Database connection failed")
return
}

// 延迟调用关闭数据库 阻止新的查询
defer db.Close()

// 开启事务
t, err := db.Begin()
if err != nil {
if t != nil {
// 回滚
t.Rollback()
}
fmt.Println("Begin transaction failed")
return
}

// 准备 SQL 语句
sqlStr := "update acl_user set nick_name = ?, age = ? where id = ? and is_deleted = 0"
// 执行 SQL 语句
_, err = t.Exec(sqlStr, "AAA", 60, 102)

if err != nil {
// 回滚
t.Rollback()
fmt.Println("Exec failed")
return
}

// 准备 SQL 语句
sqlStr2 := "update acl_user set nick_name = ?, age = ? where id = ? and is_deleted = 0"
// 执行 SQL 语句
_, err = t.Exec(sqlStr2, "BBB", 50, 103)

if err != nil {
// 回滚
t.Rollback()
fmt.Println("Exec failed")
return
}

// 提交事务
err = t.Commit()

if err != nil {
// 回滚
t.Rollback()
fmt.Println("Commit failed")
return
}

fmt.Println("Exec transaction Success")

}




特别声明:以上内容(图片及文字)均为互联网收集或者用户上传发布,本站仅提供信息存储服务!如有侵权或有涉及法律问题请联系我们。
举报
评论区(0)
按点赞数排序
用户头像
精选文章
thumb 中国研究员首次曝光美国国安局顶级后门—“方程式组织”
thumb 俄乌线上战争,网络攻击弥漫着数字硝烟
thumb 从网络安全角度了解俄罗斯入侵乌克兰的相关事件时间线