postgresql 中使用 uuid 类型需先启用 uuid-ossp 扩展,gorm 模型应配置 default:uuid_generate_v4() 交由数据库生成,推荐用 string 字段类型避免扫描兼容问题,批量查询须参数化防注入和索引失效。

PostgreSQL 中 uuid 类型必须显式启用扩展
PostgreSQL 默认不自动支持 uuid 类型的生成和校验,如果你直接在 GORM 模型里写 UUID string `gorm:"type:uuid;primaryKey"`,建表会失败并报错:ERROR: type "uuid" does not exist。这是因为 uuid-ossp 扩展未启用。
实操上必须先连进数据库执行:
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
建议放在迁移脚本最开头,或部署时统一初始化。GORM 自身不会帮你创建这个扩展——它只管 DDL,不管 extension 管理。
Gorm.Model 和 BeforeCreate 都不如 default:uuid_generate_v4()
很多人尝试在 Go 结构体里用 uuid.NewString() 赋默认值,或靠 BeforeCreate 回调生成,但这会导致主键在应用层生成,丢失数据库层面的原子性保障(比如并发插入、只读副本写入等场景可能出问题)。
更可靠的做法是把生成逻辑下推到 PostgreSQL:
- 在 GORM tag 里写
gorm:"type:uuid;primaryKey;default:uuid_generate_v4()" - 确保字段类型是
string或uuid.UUID(后者需导入github.com/google/uuid) - 如果用
uuid.UUID,GORM v1.24+ 原生支持序列化;v1.23 及更早需自定义Scanner/Valuer
这样插入时不用传 ID,PostgreSQL 自动填充,且值全局唯一、无序、抗猜测。
用 uuid.UUID 字段类型时注意扫描兼容性
PostgreSQL 返回的 uuid 值是字节数组(16 bytes),而 Go 的 github.com/google/uuid.UUID 是固定 16 字节结构体。GORM 默认能正确处理,但前提是驱动版本够新(pgx/v5 推荐 ≥ v5.4.0,lib/pq 已不推荐)。
常见错误现象:
sql: Scan error on column index 0, name "id": unsupported Scan, storing driver.Value type []uint8 into type *github.com/google/uuid.UUID- 原因:用了旧版
lib/pq或没注册uuid类型转换器
解决方法(以 pgx 为例):
import "github.com/jackc/pgx/v5/pgtype"
pgtype.RegisterDefaultPgType(uuid.UUID{}, "uuid")
或者干脆用 string 类型字段 + default:uuid_generate_v4(),省去类型适配麻烦,语义也足够清晰。
查询时用字符串比较没问题,但别用 IN 拼接大量 UUID
PostgreSQL 对 uuid 类型做了优化,用字符串查(如 WHERE id = 'a1b2c3...'::uuid)性能很好,GORM 自动生成的 SQL 也会加类型转换。
但要注意批量查询场景:
- 用
db.Where("id IN ?", ids).Find(&items)(ids []string)是安全的,GORM 会正确绑定参数 - 千万别手动拼接
"'a1...','b2...'",既易 SQL 注入,又触发隐式类型转换,可能走不到索引 - 超过 1000 个 UUID 时,考虑分批或改用临时表 JOIN,避免参数过多导致 prepare 失败
另外,PostgreSQL 的 uuid 索引是 B-tree,默认有序,但实际值随机,所以范围查询(BETWEEN)意义不大,别指望按时间顺序检索。











