生成 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:
- 单元测试方法 Test 开头
- 使用 TestMain 作为所有单元测试入口
- 连接建立使用一个单独的 postgres driver: 选择pq, 安装后通过
_
方式引入. - 使用 testify 进行单元测试 assert.
实现 Transaction
Transaction 即数据库事务,用于包裹一系列的数据库操作。一个事务就表示一个数据库的原子操作。
待实现 Transaction: 从 Account1 转账 10 到 Account2, 过程:
- 创建一个对应的 transfer 记录, 内容为 Account1 向 Account2 转账 10.
- 创建一个 Account 1 的余额变更记录, 内容为余额 -10
- 创建一个 Account 2 的余额变更记录, 内容为余额 +10
- 对 Account 1 的 Balance -10
- 对 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 组合起来使用). 视频中利用单独的方法来做.