原文:Bun: Defining models (uptrace.dev)
版本:v1.1.17
定义模型
将表映射到结构体
每个表都需要定义相应 go 结构体(模型)。Bun 会把可导出的结构体字段映射到表的列并忽略非导出的字段。
type User struct {
bun.BaseModel `bun:"table:users,alias:u"`
ID int64 `bun:"id,pk,autoincrement"`
Name string `bun:"name,notnull"`
email string // 非导出字段会被忽略
}
结构体标签
Bun 使用合理的默认值来生成名称和推导类型,但是也可以使用下面的 struct 标签覆写默认值。
| 标签 | 说明 |
|---|---|
bun.BaseModel bun:"table:table_name" | 覆写表名。 |
bun.BaseModel bun:"alias:table_alias" | 覆写默认的表别名。 |
bun.BaseModel bun:"select:view_name" | 覆写用于 SELECT 查询的表名。 |
| bun:"-" | 忽略该字段。 |
| bun:"column_name" | 覆写默认的列名。 |
| bun:"alt:alt_name" | 替换列名。在迁移时有用。 |
| bun:",pk" | 将列标记为主键并设置为 notnull 。 支持多/复合主键。 |
| bun:",autoincrement" | 在 PostgreSQL 中将列标记为 serial ,在 MySQL 中将列标记为 autoincrement ,在 MSSQL 中标记为 identity。也会设置为 nullzero 。 |
| bun:"type:uuid" | 覆写默认的 SQL 类型。 |
| bun:"default:gen_random_uuid()" | 告诉 CreateTable 设置 DEFAULT 表达式。 |
| bun:",notnull" | 告诉 CreateTable 添加 NOT NULL 约束。 |
| bun:",unique" | 告诉 CreateTable 添加 唯一约束 。 |
| bun:",unique:group_name" | 用于一组列的唯一约束。 |
| bun:",nullzero" | 将 Go 的零值转换为 SQL 的 NULL 或 DEFAULT (被支持时)。 |
| bun:",scanonly" | 在 SELECT/INSERT/UPDATE/DELETE 中仅使用该字段扫描查询结果并忽略。 |
| bun:",array" | 使用 PostgreSQL 数组。 |
| bun:",json_use_number" | 使用 json.Decoder.UseNumber 解码 JSON 。 |
| bun:",msgpack" | 使用 MessagePack 编码/解码数组。 |
DeletedAt time.Time bun:",soft_delete" | 对于当前模型使用逻辑删除。 |
表名
Bun 使用下划线连接结构体名的单词小写来生成表名和别名。也会将表名复数化,例如,结构体 ArticleCategory 会生成表名 article_categories 和别名 article_category 。
覆写生成的表名和别名:
type User struct {
bun.BaseModel `bun:"table:myusers,alias:u"`
}
为 SELECT 查询指定不同的表名:
type User struct {
bun.BaseModel `bun:"select:users_view,alias:u"`
}
ModelTableExpr 函数
使用 ModelTableExpr 函数,会覆写结构体的表名,但不会覆写别名。 ModelTableExpr 应该一直使用相同的表别名,例如:
type User struct {
bun.BaseModel `bun:"table:myusers,alias:u"`
}
// 好的写法
db.NewSelect().Model(&User{}).ModelTableExpr("all_users AS u")
db.NewSelect().Model(&User{}).ModelTableExpr("deleted_users AS u")
// 坏的写法
db.NewSelect().Model(&User{}).ModelTableExpr("all_users AS user")
db.NewSelect().Model(&User{}).ModelTableExpr("deleted_users AS deleted")
列名
Bun 使用下划线连接结构体中字段名的单词小写来生成列名。例如,结构体字段 UserID 生成的列名是 user_id 。
覆写生成的列名:
type User struct {
Name string `bun:"myname"`
}
SQL 命名转换
对于表名和列名使用 蛇形命名 。如果遇到了欺骗性的 SQL 解析错误,尝试用双引号将标识符引起来(MySQL使用反引号)再检查下问题是否已消失。
警告
不要使用 SQL 关键字 (例如 order, user) 作为标识符。
警告
不要使用区分大小写的名字,因为这样的名字会被强制转换成小写,例如,
UserOrders 会变成 userorders 。
列类型
Bun 会根据结构体字段的类型生成列类型。例如,Go 类型 string 会被转换为 SQL类型 varchar 。
覆写生成的列类型:
type User struct {
ID int64 `bun:"type:integer"`
}
NULL
要表示 SQL 的 NULL ,需要使用指针或 sql.Null* 类型:
type Item struct {
Active *bool
// or
Active sql.NullBool
}
例如:
(*bool)(nil)和sql.NullBool{}代表NULL。(*bool)(false)和sql.NullBool{Valid: true}代表FALSE。(*bool)(true)和sql.NullBool{Valid: true, Value: true}代表TRUE。
Go 的零值和 NULL
要将 Go 的零值转换为 NULL,使用 nullzero 标签:
type User struct {
Name string `bun:",nullzero"`
}
默认值
要指定默认的SQL 表达式,可使用 nullzero、notnull、 和 default 标签的组合。
type User struct {
Name string `bun:",nullzero,notnull,default:'unknown'"`
}
err := db.NewCreateTable().Model((*User)(nil)).Exec(ctx)
CREATE TABLE users (
name text NOT NULL DEFAULT 'unknown'
);
自动的时间戳
使用下面的代码可在 INSERT 时自动创建并更新时间:
type User struct {
CreatedAt time.Time `bun:",nullzero,notnull,default:current_timestamp"`
UpdatedAt time.Time `bun:",nullzero,notnull,default:current_timestamp"`
}
如果不想更新时间,可使用 bun.NullTime :
type User struct {
CreatedAt time.Time `bun:",nullzero,notnull,default:current_timestamp"`
UpdatedAt bun.NullTime
}
也可使用 钩子 设置结构体的字段:
var _ bun.BeforeAppendModelHook = (*User)(nil)
func (u *User) BeforeAppendModel(ctx context.Context, query bun.Query) error {
switch query.(type) {
case *bun.InsertQuery:
u.CreatedAt = time.Now()
case *bun.UpdateQuery:
u.UpdatedAt = time.Now()
}
return nil
}
扩展模型
可以使用 extend 标签选项向/从现有的模型中添加/删除字段。新的模型会从原始的模型继承表名和别名。
type UserWithCount struct {
User `bun:",extend"`
Name string `bun:"-"` // 删除该字段
AvatarCount int // 添加新字段
}
结构体嵌入
Bun 可以使用前缀将一个模型嵌入到另一个模型中,例如:
type Role struct {
Name string
Users Permissions `bun:"embed:users_"`
Profiles Permissions `bun:"embed:profiles_"`
Roles Permissions `bun:"embed:roles_"`
}
type Permissions struct {
View bool
Create bool
Update bool
Delete bool
}
上面的代码会创建下面的表:
CREATE TABLE roles (
name TEXT,
users_view BOOLEAN,
users_create BOOLEAN,
users_update BOOLEAN,
users_delete BOOLEAN,
profiles_view BOOLEAN,
profiles_create BOOLEAN,
profiles_update BOOLEAN,
profiles_delete BOOLEAN,
roles_view BOOLEAN,
roles_create BOOLEAN,
roles_update BOOLEAN,
roles_delete BOOLEAN
);