在 Ent 模式中使用复合类型
在 PostgreSQL 中,复合类型的结构类似于行或记录,由字段名及其对应的数据类型组成。将 Ent 字段设置为复合类型可以在单个列中存储复杂的结构化数据。
本指南将介绍如何在 Ent 模式中将模式字段类型定义为复合类型,并使用 Atlas 配置模式迁移,将复合类型和 Ent 模式作为一个迁移单元进行管理。
Atlas 对复合类型的支持仅限专业版用户。 要使用此功能,请运行:
atlas login
安装 Atlas
To install the latest release of Atlas, simply run one of the following commands in your terminal, or check out the Atlas website:
- macOS + Linux
- Homebrew
- Docker
- Windows
curl -sSf https://atlasgo.sh | sh
brew install ariga/tap/atlas
docker pull arigaio/atlas
docker run --rm arigaio/atlas --help
If the container needs access to the host network or a local directory, use the --net=host flag and mount the desired
directory:
docker run --rm --net=host \
-v $(pwd)/migrations:/migrations \
arigaio/atlas migrate apply
--url "mysql://root:pass@:3306/test"
Download the latest release and move the atlas binary to a file location on your system PATH.
登录 Atlas
$ atlas login a8m
您已成功连接到 Atlas Cloud 上的 "a8m" 账户。
复合模式
ent/schema 包主要用于定义 Ent 类型(对象)、它们的字段、边和逻辑。复合类型或任何其他数据库对象在 Ent 模型中并没有对应的表示——复合类型只需定义一次,便可在不同的字段和模型中多次使用。
为了扩展我们的 PostgreSQL 模式以包含自定义复合类型和 Ent 类型,我们配置 Atlas 从复合模式数据源读取模式状态。按照以下步骤为您的项目进行配置:
- 创建一个
schema.sql文件,定义必要的复合类型。同样,您也可以在 Atlas Schema HCL 语言中配置复合类型:
- 使用 SQL
- 使用 HCL
CREATE TYPE address AS (
street text,
city text
);
schema "public" {}
composite "address" {
schema = schema.public
field "street" {
type = text
}
field "city" {
type = text
}
}
- 在 Ent 模式中,定义一个仅在 PostgreSQL 方言中使用复合类型的字段:
- 模式
- 地址类型
// User 的字段。
func (User) Fields() []ent.Field {
return []ent.Field{
field.String("address").
GoType(&Address{}).
SchemaType(map[string]string{
dialect.Postgres: "address",
}),
}
}
如果带有自定义驱动特定类型的模式与其他数据库一起使用,Ent 将回退到驱动使用的默认类型(例如 "varchar")。
type Address struct {
Street, City string
}
var _ field.ValueScanner = (*Address)(nil)
// Scan 实现 database/sql.Scanner 接口。
func (a *Address) Scan(v interface{}) (err error) {
switch v := v.(type) {
case nil:
case string:
_, err = fmt.Sscanf(v, "(%q,%q)", &a.Street, &a.City)
case []byte:
_, err = fmt.Sscanf(string(v), "(%q,%q)", &a.Street, &a.City)
}
return
}
// Value 实现 driver.Valuer 接口。
func (a *Address) Value() (driver.Value, error) {
return fmt.Sprintf("(%q,%q)", a.Street, a.City), nil
}
- 创建一个简单的
atlas.hcl配置文件,其中包含一个composite_schema,该数据源同时包含您在schema.sql中定义的自定义类型和您的 Ent 模式:
data "composite_schema" "app" {
# 首先加载自定义类型。
schema "public" {
url = "file://schema.sql"
}
# 其次加载 Ent 模式。
schema "public" {
url = "ent://ent/schema"
}
}
env "local" {
src = data.composite_schema.app.url
dev = "docker://postgres/15/dev?search_path=public"
}
使用方式
设置好模式后,我们可以使用 atlas schema inspect 命令获取其表示形式,为其生成迁移,将其应用到数据库等。以下是一些帮助您开始使用 Atlas 的命令:
检查模式
atlas schema inspect 命令通常用于检查数据库。但我们也可以使用它来检查我们的 composite_schema 并打印其 SQL 表示形式:
atlas schema inspect \
--env local \
--url env://src \
--format '{{ sql . }}'
上述命令会打印以下 SQL。请注意,address 复合类型在 address 字段中使用之前已在模式中定义:
-- 创建复合类型 "address"
CREATE TYPE "address" AS ("street" text, "city" text);
-- 创建 "users" 表
CREATE TABLE "users" ("id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, "address" "address" NOT NULL, PRIMARY KEY ("id"));
为模式生成迁移
要生成模式的迁移,请运行以下命令:
atlas migrate diff \
--env local
注意,此时会创建一个新的迁移文件,内容如下:
-- 创建复合类型 "address"
CREATE TYPE "address" AS ("street" text, "city" text);
-- 创建 "users" 表
CREATE TABLE "users" ("id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, "address" "address" NOT NULL, PRIMARY KEY ("id"));
应用迁移
要将上面生成的迁移应用到数据库,请运行以下命令:
atlas migrate apply \
--env local \
--url "postgres://postgres:pass@localhost:5432/database?search_path=public&sslmode=disable"
有时需要直接将模式应用到数据库而不生成迁移文件。例如,在试验模式更改、启动测试数据库等情况下。在这种情况下,您可以使用以下命令直接将模式应用到数据库:
atlas schema apply \
--env local \
--url "postgres://postgres:pass@localhost:5432/database?search_path=public&sslmode=disable"
或者,使用 Atlas Go SDK:
ac, err := atlasexec.NewClient(".", "atlas")
if err != nil {
log.Fatalf("failed to initialize client: %w", err)
}
// 自动使用所需模式更新数据库。
// 另一种选择是手动使用 'migrate apply' 或 'schema apply'。
if _, err := ac.SchemaApply(ctx, &atlasexec.SchemaApplyParams{
Env: "local",
URL: "postgres://postgres:pass@localhost:5432/database?search_path=public&sslmode=disable",
AutoApprove: true,
}); err != nil {
log.Fatalf("failed to apply schema changes: %w", err)
}
本指南的代码可以在 GitHub 上找到。