[官文译]Go轻量级ORM Bun - 定义模型

379 阅读4分钟

原文: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 关键字 (例如 orderuser) 作为标识符。

警告

不要使用区分大小写的名字,因为这样的名字会被强制转换成小写,例如, 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 表达式,可使用 nullzeronotnull、 和 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
);

其它相关链接