Skip to main content

生成 Go 数据库访问接口和实现

数据库操作即对特定表进行 CRUD. 在 Go 中实现 CRUD 有如下方法:

  • 标准库 API: 最基础的 sql 包.
    • 优点:
      • 速度快
    • 缺点:
      • 手动编写 SQL
      • 手动处理对象映射
  • GORM: ORM 框架
    • 优点:
      • 实现代码简单
      • 快速开发
    • 缺点:
      • 细节隐藏
      • 对复杂查询支持差
      • 性能差(大吞吐量时性能只有标准库 API 的 1/5)
  • SQLX 库: 更偏向底层
    • 优点:
      • 速度快
    • 缺点:
      • 写法复杂
      • 错误在运行时暴露
  • SQLC 库: 平衡的方案
    • 优点:
      • 速度快
      • 代码自动生成
      • 手写 SQL
      • 错误在编译时暴露
    • 缺点:
      • 支持数据库较少(当前正式支持仅 Postgres)

因此选择 SQLC 进行, 它的官方文档.

使用 SQLC 生成接口

macOS 安装 SQLC: brew install sqlc.

进入工程目录后初始化: sqlc init 以生成初始配置文件, 在官方仓库可找到配置模板, 类似如下:

version: "1"
packages:
- name: "db" # 生成包名
path: "internal/db" # 生成包路径
queries: "./sql/query/" # 查询 SQL 存放位置
schema: "./sql/schema/" # 定义 SQL 存放位置
engine: "postgresql"
emit_json_tags: true
emit_prepared_queries: true
emit_interface: false
emit_exact_table_names: false
emit_empty_slices: false
emit_exported_queries: false
emit_result_struct_pointers: false
emit_params_struct_pointers: false
emit_methods_with_db_argument: false
emit_enum_valid_method: false
emit_all_enum_values: false
json_tags_case_style: "camel"
output_db_file_name: "db.go"
output_models_file_name: "models.go"
output_querier_file_name: "querier.go"

正确配置后, 利用 sqlc generate 生成. Query 写法详见官方文档.

编写单元测试

单元测试的 convention:

  1. 单元测试方法 Test 开头
  2. 使用 TestMain 作为所有单元测试入口
  3. 连接建立使用一个单独的 postgres driver: 选择pq, 安装后通过 _ 方式引入.
  4. 使用 testify 进行单元测试 assert.

实现 Transaction

Transaction 即数据库事务,用于包裹一系列的数据库操作。一个事务就表示一个数据库的原子操作。

待实现 Transaction: 从 Account1 转账 10 到 Account2, 过程:

  1. 创建一个对应的 transfer 记录, 内容为 Account1 向 Account2 转账 10.
  2. 创建一个 Account 1 的余额变更记录, 内容为余额 -10
  3. 创建一个 Account 2 的余额变更记录, 内容为余额 +10
  4. 对 Account 1 的 Balance -10
  5. 对 Account 2 的 Balance +10

使用 Transaction 有两个主要原因:

  • 提供可靠且确保一致的原子操作(即便系统失效也可保证转账数据正常)
  • 保证对数据库的并发访问不会出现问题

为满足上述两点, Transaction 必须具备 ACID:

  • Atomicity(A): 操作是原子的, 即要么全部执行成功, 要么只要是失败就保证回滚, 且数据库是完全没有被更改的
  • Consistency(C): 执行 Transaction 后的数据库状态必须是一致的, 即根据预定义规则检查了所有写入数据库的数据都是合法的.
  • Isolation(I): 同时运行的不同事务间不会相互影响. Isolation 有四个等级, 不同等级的 Isolation 决定了一个事务的改动对另外一个事务的可见性.
  • Durability(D): 执行成功的事务的改动必须被持久化, 不丢失, 即便是遇到系统失效.

Transaction 的两种形式:

BEGIN;
...
COMMIT;

BEGIN;
...
ROLLBACK;

在 Go 中可定义 Transaction 专用的操作方法(将多个 query 组合起来使用). 视频中利用单独的方法来做.