与数据库打交道

够浪的官方database/sql 库实际只提供了一套操作数据库的接口和规范。如 抽象好的SQL预处理(prepare),连接池管理,数据绑定,事务,错误处理等等,并未提供具体某种数据库实现的协议支持。

database/sql

DB 扮演的角色与laravel数据库管理实例,同nodejs中的sequelizejs连接大抵一样,都是掌权者,负责指挥不干活。

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

是故需要匿名导入包含init函数的驱动包,实现官方接口

func init() {
    sql.Register("mysql", &MySQLDriver{})
}

sql.Open返回的DB是这货

type Conn interface {
    Prepare(query string) (Stmt, error)
    Close() error
    Begin() (Tx, error)
}

Placeholder

不同类型的数据库占位符,有所不同

MySQL               PostgreSQL            Oracle
=====               ==========            ======
WHERE col = ?       WHERE col = $1        WHERE col = :col
VALUES(?, ?, ?)     VALUES($1, $2, $3)    VALUES(:val1, :val2, :val3)

DB

package main

import (
    "database/sql"
    _ "github.com/go-sql-driver/mysql"
    "log"
)

// sql.DB sql.Rows sql.Scan
func main() {
    // *sql.DB
    db, err := sql.Open("mysql", "root:@tcp(127.0.0.1:3306)/shop58")
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()

    var (
        id    int
        name  string
        email string
    )

    // *sql.Rows
    rows, err := db.Query("select id, name,email from users where id = ?", 1)
    if err != nil {
        log.Fatal(err)
    }
    // 防止进一步枚举(换而言之,读完会释放连接资源),很重要!!!
    defer rows.Close()

    // 读取下一行结果集
    // 必须要把 rows 里的内容读完,或者显式调用 Close() 方法,
    // 否则在 defer 的 rows.Close() 执行之前,连接永远不会释放
    for rows.Next() {
        err := rows.Scan(&id, &name, &email)
        if err != nil {
            log.Fatal(err)
        }
        log.Println(id, name, email)
    }
    err = rows.Err()
    if err != nil {
        log.Fatal(err)
    }
}

Tx

事务在够浪也有涉及,官方仅一个接口规范,管理了一个标准库

package main

import (
    "database/sql"
    _ "github.com/go-sql-driver/mysql"
    "log"
)

func main() {
    db, err := sql.Open("mysql", "root:@tcp(127.0.0.1:3306)/test")
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()

    // *sql.Tx
    tx, err := db.Begin()
    if err != nil {
        log.Fatal(err)
    }
    defer tx.Rollback()

    // *sql.Stmt
    stmt, err := tx.Prepare("insert into account (id,price) values (null,?)")
    if err != nil {
        log.Fatal(err)
    }
    defer stmt.Close()

    for i := 0; i < 5; i++ {
        _, err = stmt.Exec(i)
        if err != nil {
            log.Fatal(err)
        }
    }
    err = tx.Commit()
    if err != nil {
        log.Fatal(err)
    }
    stmt.Close()
}

官方

包括该库的功能介绍、用法、注意事项和一些非人类方法实现,参阅 http://go-database-sql.org/
如反直觉的一些实现方式(同一个goroutine内对sql.DB的查询,可能在多个连接上)。

官方东西总是大道至简,够用,多点操作,可能就要用到高效率的ORM和SQL Builder。这些工具屏蔽了DB层一些细节,让你几乎感觉不到数据库的存在,只是使用面向对象的编码。中小型公司可能还凑合,到大厂不行了。比如某些orm会生成的SQL语句会自动limit 1000,这是后话。

本作品采用《CC 协议》,转载必须注明作者和本文链接
pardon110
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
开发者 @ 社科大
文章
134
粉丝
24
喜欢
101
收藏
55
排名:106
访问:8.9 万
私信
所有博文
社区赞助商