《Go语言四十二章经》第三十九章 Mysql数据库

    Go 提供了database/sql包用于对SQL数据库的访问,作为操作数据库的入口对象sql.DB,主要为我们提供了两个重要的功能:

    • sql.DB 通过数据库驱动为我们提供管理底层数据库连接的打开和关闭操作.
    • sql.DB 为我们管理数据库连接池

    需要注意的是,sql.DB表示操作数据库的抽象访问接口, 而非一个数据库连接对象;它可以根据driver打开关闭数据库连接,管理连接池。正在使用的连接被标记为繁忙,用完后回到连接池等待下次使用。所以,如果你没有把连接释放回连接池,会导致过多连接使系统资源耗尽。

    导入mysql数据库驱动

    39.2 Mysql数据库操作

    我们先建立表结构:

    1. `cid` int(10) NOT NULL AUTO_INCREMENT,
    2. `cname` varchar(60) NOT NULL,
    3. `ename` varchar(100),
    4. `cateimg` varchar(255),
    5. `addtime` int(10) unsigned NOT NULL DEFAULT '0',
    6. `publishtime` int(10) unsigned NOT NULL DEFAULT '0',
    7. `scope` int(10) unsigned NOT NULL DEFAULT '10000',
    8. `status` tinyint(1) unsigned NOT NULL DEFAULT '0',
    9. PRIMARY KEY (`cid`),
    10. UNIQUE KEY catename (`cname`)
    11. ) ENGINE=InnoDB AUTO_INCREMENT=99 DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;

    下面代码使用预编译的方式,来进行增删改查的操作,并通过事务来批量提交一批数据。预编译语句(PreparedStatement)提供了诸多好处,可以实现自定义参数的查询,通常来说,比手动拼接字符串 SQL 语句高效,可以防止SQL注入攻击。

    每次db.Query操作后,都建议调用rows.Close()。 因为 db.Query() 会从数据库连接池中获取一个连接,这个底层连接在结果集(rows)未关闭前会被标记为处于繁忙状态。当遍历读到最后一条记录时,会发生一个内部EOF错误,自动调用rows.Close(), 但如果提前退出循环,rows不会关闭,连接不会回到连接池中,连接也不会关闭,则此连接会一直被占用。 因此通常我们使用 defer rows.Close() 来确保数据库连接可以正确放回到连接池中。

    1. // 插入数据,sql预编译
    2. func (dbw *DbWorker) insertData() {
    3. stmt, _ := dbw.Db.Prepare(`INSERT INTO t_article_cate (cname, addtime, scope) VALUES (?, ?, ?)`)
    4. defer stmt.Close()
    5. ret, err := stmt.Exec("栏目1", time.Now().Unix(), 10)
    6. // 通过返回的ret可以进一步查询本次插入数据影响的行数
    7. // RowsAffected和最后插入的Id(如果数据库支持查询最后插入Id)
    8. if err != nil {
    9. return
    10. }
    11. fmt.Println("LastInsertId:", LastInsertId)
    12. }
    13. if RowsAffected, err := ret.RowsAffected(); nil == err {
    14. fmt.Println("RowsAffected:", RowsAffected)
    15. }
    16. }

    删除数据:

    修改数据:

    1. // 修改数据,预编译
    2. func (dbw *DbWorker) editData() {
    3. stmt, err := dbw.Db.Prepare(`UPDATE t_article_cate SET scope=? WHERE cid=?`)
    4. ret, err := stmt.Exec(111, 123)
    5. // 通过返回的ret可以进一步查询本次插入数据影响的行数RowsAffected和
    6. // 最后插入的Id(如果数据库支持查询最后插入Id).
    7. if err != nil {
    8. fmt.Printf("insert data error: %v\n", err)
    9. return
    10. }
    11. if RowsAffected, err := ret.RowsAffected(); nil == err {
    12. fmt.Println("RowsAffected:", RowsAffected)
    13. }
    14. }

    查询数据:

    1. func (dbw *DbWorker) transaction() {
    2. tx, err := dbw.Db.Begin()
    3. if err != nil {
    4. return
    5. }
    6. defer tx.Rollback()
    7. stmt, err := tx.Prepare(`INSERT INTO t_article_cate (cname, addtime, scope) VALUES (?, ?, ?)`)
    8. if err != nil {
    9. fmt.Printf("insert data error: %v\n", err)
    10. return
    11. }
    12. for i := 100; i < 110; i++ {
    13. cname := strings.Join([]string{"栏目-", string(i)}, "-")
    14. _, err = stmt.Exec(cname, time.Now().Unix(), i+20)
    15. if err != nil {
    16. fmt.Printf("insert data error: %v\n", err)
    17. return
    18. }
    19. }
    20. err = tx.Commit()
    21. if err != nil {
    22. fmt.Printf("insert data error: %v\n", err)
    23. return
    24. }
    25. stmt.Close()