使用sqlc向PostgreSQL jsonb字段插入[]byte数组失败求助
解决sqlc生成[]byte类型存入PostgreSQL jsonb字段报错问题
问题背景
我是Go与sqlc的新手,PostgreSQL数据库中有一个jsonb字段。执行sqlc generate后生成的类型为[]byte数组(与sqlc文档描述一致),但调用生成的插入函数将[]byte存入该字段时,抛出“Not a valid JSON type”错误。由于生成代码不能修改(重新生成会覆盖),我尝试让sqlc生成自定义类型而非[]byte,但不清楚如何将自定义结构体转换为PostgreSQL可接受的jsonb格式。
相关代码
sqlc生成配置SQL
-- name: CreateTeamUser :one INSERT INTO team_users ( meta_jsonb ) VALUES ( $1 ) RETURNING *;
生成的Go代码
type CreateTeamUserParams struct { Meta_jsonb []byte `json:"meta_jsonb"` } func (q *Queries) CreateTeamUser(ctx context.Context, arg CreateTeamUserParams) (TeamUser, error) { row := q.db.QueryRow(ctx, createTeamUser, arg.Meta_jsonb, ) var i TeamUser err := row.Scan( &i.ID, &i.Meta_jsonb ) return i, err }
调用代码
arg := db.CreateTeamUserParams{ Meta_jsonb: user.GetMeta_Jsonb(), } _, err := server.store.CreateTeamUser(ctx, arg)
传入的数据
{"meta_jsonb": {"name": "me"}}
报错信息
ERR received a HTTP request body="{\"code\":13,\"message\":\"failed to create user: ERROR: invalid input syntax for type json (SQLSTATE 22P02)\"}"
解决方案
方案一:手动序列化结构体为合法JSON字节流
报错的核心原因是user.GetMeta_Jsonb()返回的不是合法的JSON格式字节数组(比如直接返回了Go结构体而非序列化后的字节)。只需将结构体序列化为JSON字节再传入即可:
import "encoding/json" // ... metaData := user.GetMeta_Jsonb() metaBytes, err := json.Marshal(metaData) if err != nil { // 处理序列化错误,比如返回HTTP 400 } arg := db.CreateTeamUserParams{ Meta_jsonb: metaBytes, } _, err = server.store.CreateTeamUser(ctx, arg)
方案二:让sqlc生成自定义类型并自动处理转换
通过定义实现driver.Valuer和sql.Scanner接口的自定义类型,让sqlc自动完成Go类型与PostgreSQL jsonb的转换:
- 定义自定义类型(放在sqlc生成的包目录下,比如
db/types.go):
package db import ( "database/sql/driver" "encoding/json" "errors" ) // 替换成你的结构体字段 type Meta struct { Name string `json:"name"` // 其他需要的字段 } // Value 将Meta转换为数据库可接受的JSON字节 func (m Meta) Value() (driver.Value, error) { return json.Marshal(m) } // Scan 将数据库返回的jsonb字节转换为Meta类型 func (m *Meta) Scan(value interface{}) error { bytes, ok := value.([]byte) if !ok { return errors.New("无法将值转换为Meta类型") } return json.Unmarshal(bytes, m) }
- 修改sqlc配置文件(
sqlc.yaml),添加类型映射:
version: "2" sql: - schema: "schema.sql" queries: "queries.sql" engine: "postgresql" gen: go: package: "db" out: "db" overrides: - column: "team_users.meta_jsonb" go_type: "Meta"
- 重新生成代码
执行sqlc generate后,生成的CreateTeamUserParams中Meta_jsonb的类型会变为Meta,调用时直接传入结构体即可:
arg := db.CreateTeamUserParams{ Meta_jsonb: db.Meta{Name: "me"}, } _, err := server.store.CreateTeamUser(ctx, arg)
内容的提问来源于stack exchange,提问作者The Keeper
相关产品推荐
相关产品推荐

