当前位置: 代码网 > it编程>前端脚本>Golang > golang的database.sql包和事务处理操作步骤

golang的database.sql包和事务处理操作步骤

2025年04月24日 Golang 我要评论
事务(transaction)事务是数据库操作中的一个逻辑单元,由一系列的数据库操作组成。这一系列操作要么全部执行并且提交,要么全部回滚,确保数据的一致性和完整性。事务具有以下四个主要特性,通常被称为

事务(transaction)

事务是数据库操作中的一个逻辑单元,由一系列的数据库操作组成。这一系列操作要么全部执行并且提交,要么全部回滚,确保数据的一致性和完整性。事务具有以下四个主要特性,通常被称为acid特性:

  • 原子性(atomicity):事务作为一个整体,所有的操作要么全部执行,要么全部不执行,不会出现部分执行的情况。
  • 一致性(consistency):在事务开始和结束时,数据库的状态都是一致的,符合预定的约束条件。
  • 隔离性(isolation):一个事务的执行不影响另一个事务的执行,除非另一个事务等待第一个事务完成。
  • 持久性(durability):一旦事务提交,所做的修改就会被永久保存在数据库中,不会因系统故障或崩溃而丢失。

在go语言中,使用database/sql包进行事务的基本步骤如下:

  • 开始事务:使用db.begin()方法启动一个事务,返回一个事务对象tx
  • 执行sql操作:使用事务对象tx执行多个sql操作,如tx.exec()tx.query()等。
  • 提交或回滚事务:如果所有sql操作都成功执行,使用tx.commit()提交事务;如果出现错误,使用tx.rollback()回滚事务。

事务的使用场景

  • 转账操作:转账操作涉及从一个账户扣款和另一个账户加款,这两个操作必须同时成功,否则数据会不一致。
  • 订单处理:在提交订单时,可能需要减少库存并增加订单记录,这些操作需要保证同时成功。
  • 批量操作:在批量插入、更新或删除数据时,确保所有操作要么全部执行,要么全部回滚。

注意事项

  • 尽量减少事务的粒度:长时间的事务会占用数据库资源,可能导致其他操作被阻塞。尽量确保事务中的操作只涉及必要的数据库访问。
  • 避免在事务中进行长时间的操作:如果在事务中进行文件io、网络请求等长时间操作,可能会导致数据库连接被长时间占用,影响系统性能。
  • 正确处理事务的提交和回滚:确保在所有可能的错误情况下,事务能够正确回滚,并释放相关资源。

数据库连接方法

dsn := "username:password@tcp(127.0.0.1:3306)/dbname"
方法名描述示例
sql.open()打开数据库连接db, err := sql.open("mysql", dsn)
db.ping()测试数据库连接是否有效err = db.ping()
db.close()关闭数据库连接defer db.close()

事务方法

方法名描述示例
db.begin()开始一个事务tx, err := db.begin()
tx.rollback()回滚事务tx.rollback()
tx.commit()提交事务err = tx.commit()

查询和执行方法

方法名描述示例
tx.exec()执行不返回结果的sql语句,用于create、insert、update、delete等操作tx.exec("create table ...")
tx.query()执行返回多行结果的sql查询rows, err := tx.query("select ...")
tx.queryrow()执行返回单行结果的sql查询tx.queryrow("select ...")
stmt.exec()使用预处理语句执行sql语句stmt.exec("f", "g")

预处理语句

方法名描述示例
tx.prepare()创建预处理语句stmt, err := tx.prepare(...)
stmt.close()关闭预处理语句defer stmt.close()

查询结果处理

方法名描述示例
rows.next()逐行迭代查询结果rows.next()
rows.scan()将当前行的列值赋值给变量rows.scan(&s1, &s2)
rows.err()检查查询和迭代过程中的错误rows.err()
rows.close()关闭结果集,释放相关资源defer rows.close()

预处理语句(prepared statements)

预处理语句是指在数据库中提前编译和优化的sql语句模板,可以在之后多次重复使用。预处理语句的主要优点如下:

  • 提高效率:数据库可以提前编译和优化预处理语句,减少每次执行sql时的解析时间,特别是在需要多次执行相同sql语句时。
  • 防止sql注入:通过参数化的sql语句,用户输入的数据不会直接嵌入到sql语句中,降低了sql注入的风险。
  • 减少网络开销:在需要多次执行相同的sql语句时,客户端只需要发送参数,不需要每次都发送完整的sql语句,减少网络通信的数据量。

在go语言中,使用预处理语句的基本步骤如下:

  • 准备预处理语句:使用tx.prepare()方法创建一个预处理语句对象stmt
  • 执行预处理语句:使用stmt.exec()方法执行预处理语句,传递参数。
  • 关闭预处理语句:执行完毕后,使用stmt.close()方法释放相关资源。

以下是一个使用预处理语句的示例:

stmt, err := tx.prepare("insert into table1 (column1, column2) values(?, ?)")  
if err != nil {  
    fmt.printf("准备预处理语句失败:%v\n", err)  
    return  
}  
defer stmt.close()  
// 第一次执行  
_, err = stmt.exec("f", "g")  
if err != nil {  
    tx.rollback()  
    fmt.printf("执行预处理语句第一次失败:%v\n", err)  
    return  
}  
// 第二次执行  
_, err = stmt.exec("h", "i")  
if err != nil {  
    tx.rollback()  
    fmt.printf("执行预处理语句第二次失败:%v\n", err)  
    return  
}  

在这个示例中,预处理语句一次创建,多次执行,提升了效率,并降低了sql注入的风险。

预处理语句的使用场景

  • 批量插入:在需要插入大量数据时,使用预处理语句可以显著提高效率。
  • 频繁执行相同sql语句:在需要多次执行相同的sql语句时,使用预处理语句可以减少数据库的解析开销,提高执行速度。
  • 防止sql注入:在处理用户输入的数据时,使用预处理语句可以有效防止sql注入攻击。

注意事项

  • 及时关闭预处理语句:使用完预处理语句后,记得及时关闭,释放数据库资源,避免资源泄漏。
  • 正确处理参数:确保传递给预处理语句的参数类型和数量与预处理语句中的占位符相匹配。
  • 避免过度使用:虽然预处理语句有诸多优势,但在不需要多次执行同一sql语句的情况下,创建预处理语句可能会带来额外的开销,影响性能。

总的示例代码:

package main  
import (  
    "database/sql"  
    "fmt"  
    _ "github.com/go-sql-driver/mysql"  
)  
const (  
    dsn = "username:password@tcp(localhost:3306)/test"  
)  
func main() {  
    db, err := sql.open("mysql", dsn)  
    if err != nil {  
        fmt.printf("打开数据库连接失败:%v\n", err)  
        return  
    }  
    defer db.close()  
    if err := db.ping(); err != nil {  
        fmt.printf("数据库连接不可用:%v\n", err)  
        return  
    }  
    transactionexample(db)  
}  
func transactionexample(db *sql.db) {  
    tx, err := db.begin()  
    if err != nil {  
        fmt.printf("开始事务失败:%v\n", err)  
        return  
    }  
    defer func() {  
        if err != nil {  
            fmt.println("事务回滚中...")  
            rollbackerr := tx.rollback()  
            if rollbackerr != nil && rollbackerr != sql.errtxdone {  
                fmt.printf("事务回滚失败:%v\n", rollbackerr)  
            }  
        }  
    }()  
    // 创建表  
    fmt.println("创建表...")  
    err = createtable(tx)  
    if err != nil {  
        return  
    }  
    // 插入数据  
    fmt.println("插入数据到table1...")  
    err = insertdata(tx)  
    if err != nil {  
        return  
    }  
    // 更新数据  
    fmt.println("更新table1的值...")  
    err = updatedata(tx)  
    if err != nil {  
        return  
    }  
    // 删除数据  
    fmt.println("删除数据...")  
    err = deletedata(tx)  
    if err != nil {  
        return  
    }  
    // 查询多行数据  
    fmt.println("查询多行数据...")  
    err = querymultirows(tx)  
    if err != nil {  
        return  
    }  
    // 查询单行数据  
    fmt.println("查询单行数据...")  
    err = querysinglerow(tx)  
    if err != nil {  
        return  
    }  
    // 预处理语句插入数据  
    fmt.println("使用预处理语句插入数据...")  
    err = insertwithprepare(tx)  
    if err != nil {  
        return  
    }  
    // 提交事务  
    fmt.println("提交事务...")  
    err = tx.commit()  
    if err != nil {  
        fmt.printf("提交事务失败:%v\n", err)  
        return  
    }  
    fmt.println("事务处理成功。")  
}  
func createtable(tx *sql.tx) error {  
    _, err := tx.exec("create table if not exists table1 (column1 nchar(10), column2 nchar(10))")  
    return err  
}  
func insertdata(tx *sql.tx) error {  
    _, err := tx.exec("insert into table1 (column1, column2) values ('a','b'), ('c','d'), ('e','f')")  
    return err  
}  
func updatedata(tx *sql.tx) error {  
    _, err := tx.exec("update table1 set column1 = 'c' where column1 = 'a'")  
    return err  
}  
func deletedata(tx *sql.tx) error {  
    _, err := tx.exec("delete from table1 where column1 = 'b'")  
    return err  
}  
func querymultirows(tx *sql.tx) error {  
    rows, err := tx.query("select * from table1")  
    if err != nil {  
        return err  
    }  
    defer rows.close()  
    for rows.next() {  
        var s1, s2 string  
        err := rows.scan(&s1, &s2)  
        if err != nil {  
            return fmt.errorf("扫描失败:%v", err)  
        }  
        fmt.printf("table1数据:%s, %s\n", s1, s2)  
    }  
    if err := rows.err(); err != nil {  
        return fmt.errorf("遍历table1失败:%v", err)  
    }  
    return nil  
}  
func querysinglerow(tx *sql.tx) error {  
    var c string  
    err := tx.queryrow("select column1 from table1 where column1 = 'e'").scan(&c)  
    if err != nil {  
        if err == sql.errnorows {  
            fmt.println("没有找到匹配的行。")  
        } else {  
            return fmt.errorf("查询单行数据失败:%v", err)  
        }  
        return nil  
    }  
    fmt.printf("单行数据:%s\n", c)  
    return nil  
}  
func insertwithprepare(tx *sql.tx) error {  
    stmt, err := tx.prepare("insert into table1 (column1, column2) values(?, ?)")  
    if err != nil {  
        return fmt.errorf("准备预处理语句失败:%v", err)  
    }  
    defer stmt.close()  
    _, err = stmt.exec("f", "g")  
    if err != nil {  
        return fmt.errorf("执行预处理语句失败:%v", err)  
    }  
    return nil  
}  

到此这篇关于golang的database.sql包和事务处理操作步骤的文章就介绍到这了,更多相关golang的database.sql包内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com