许多应用程序都受益于用关系结构表示一些数据,而用JSON这样更灵活的文档结构表示一些数据。利用关系型数据库中的JSON功能可以减少对专用对象数据库的需求,最大限度地减少基础设施和应用的复杂性,并提高性能。
现代SQL数据库,如PostgreSQL和CockroachDB支持这两种模式,并对JSON文档的存储、构建和操作提供本地支持。Go语言和pgx数据库驱动也包括处理关系型和文档型数据的功能。
在这个例子中,我们将在一个使用CockroachDB作为数据存储的简单Go应用程序中建立JSON数据模型。我们将使用一个简化的产品表,用于购物应用。每个产品都有一个SKU和一个名称。但是产品还有很多其他的属性,比如颜色、尺寸、重量、格式和容量,只适用于某些产品。我们将使用一列来表示所有这些额外的属性。
create table products (
sku text primary key,
name text not null,
extra_attributes jsonb not null
);
insert into products (sku, name, extra_attributes)
values
('A1000-BK', 'A1000 Battery', '{"color": "black", "capacity": 4000}'),
('A1200-RD', 'A1200 Battery', '{"color": "red", "capacity": 5500}'),
('TBLTCASE-10', 'Tablet Case', '{"size": "10 inch", "material": "Leather"}')
;
下面的代码片断将假定已经建立了数据库连接。关于连接设置的更多信息,请参阅www.cockroachlabs.com/docs/v20.2/…。
我们可以使用string 或[]byte 来读取或写入一个数据库jsonb 类型。然后我们可以使用encoding/json 包将其转换为我们的应用程序数据类型。然而,有了pgx,这就没有必要了。pgx可以自动地将数值marshal和unmarshal成JSON。
type Product struct {
SKU string
Name string
ExtraAttributes map[string]interface{}
}
// ...
var product Product
err = conn.QueryRow(
context.Background(),
"select * from products where sku = $1",
"A1000-BK",
).Scan(&product.SKU, &product.Name, &product.ExtraAttributes)
if err != nil {
// handle error
}
fmt.Printf("%#v\n", product.ExtraAttributes) // => map[string]interface {}{"capacity":4000, "color":"black"}
一个map[string]interface{} ,可以处理任何JSON对象,但在工作中可能有点尴尬。如果我们提前知道对象的结构,我们可以直接读入Go结构。
type BatteryAttributes struct {
Color string `json:"color"`
Capacity int32 `json:"capacity"`
}
type Battery struct {
SKU string
Name string
ExtraAttributes BatteryAttributes
}
// ...
err = conn.QueryRow(
context.Background(),
"select * from products where sku = $1",
"A1000-BK",
).Scan(&battery.SKU, &battery.Name, &battery.ExtraAttributes)
if err != nil {
// handle error
}
fmt.Printf("%#v\n", battery.ExtraAttributes)
// => BatteryAttributes{Color:"black", Capacity:4000}
我们也可以直接从数据库中的关系数据建立JSON文档。例如,我们可以使用jsonb_agg 和jsonb_build_object 函数来建立一个列出所有产品的 JSON 文档。
var buf []byte
err = conn.QueryRow(
context.Background(),
"select jsonb_agg(jsonb_build_object('sku', sku, 'name', name)) from products",
).Scan(&buf)
if err != nil {
// handle error
}
fmt.Println(string(buf))
// => [{"name": "A1000 Battery", "sku": "A1000-BK"}, {"name": "A1200 Battery", "sku": "A1200-RD"}, {"name": "Tablet Case", "sku": "TBLTCASE-10"}]
这种方法既可以简化Go层,又可以提高性能。
总之,利用像CockroachDB这样的关系型数据库中的JSON功能可以减少对专用对象数据库的需求,最大限度地减少基础设施和应用的复杂性,并提高性能。