ORM 简介
除了数据库查询构建器,Lucid 还拥有构建在 [活动记录模式] (en.wikipedia.org/wiki/Active_recor...) 之上的数据模型。
Lucid 的数据模型层使执行 CRUD 操作、管理模型之间的关系和定义生命周期挂钩变得超级容易。
我们建议广泛使用模型,并针对特定用例使用标准查询构建器。
什么是活动记录模式?
Active Record 也是 Ruby on Rails 使用的 ORM 的名称。然而,活动记录模式是一个更广泛的概念,任何编程语言或框架都可以实现。
注:
每当我们说活动记录这个词时,我们谈论的是模式本身,而不是 Rails 的实现。
活动记录模式提倡将数据库交互封装到特定语言的对象或类中。每个数据库表都有它的模型,该类的每个实例代表一个表行。
数据模型清理了许多数据库交互,因为你可以在模型中编码大部分行为,而不是在代码库中的任何地方编写它。
例如,你的users
表有一个日期字段,你希望在将其发送回客户端之前对其进行格式化。 这是你的代码在不使用数据模型的情况下的样子。
import { DateTime } from 'luxon'
const users = await Database.from('users').select('*')
return users.map((user) => {
user.dob = DateTime.fromJSDate(user.dob).toFormat('dd LLL yyyy')
return user
})
使用数据模型时,你可以在模型中对日期格式化操作进行编码,而不是在你获取和返回用户的任何地方编写它。
import { DateTime } from 'luxon'
import { BaseModel, column } from '@ioc:Adonis/Lucid/Orm'
class User extends BaseModel {
@column.date({
serialize: (value) => value.toFormat('dd LLL yyyy')
})
public dob: DateTime
}
并按如下方式使用它:
const users = await User.all()
return users.map((user) => user.toJSON()) // 日期在`toJSON`调用期间被格式化
创建你的第一个模型
假设你已经拥有 Lucid 设置,请运行以下命令来创建你的第一个数据模型。
node ace make:model User
# CREATE: app/Models/User.ts
您还可以通过定义-m
标志在模型旁边生成迁移。
node ace make:model User -m
# CREATE: database/migrations/1618903673925_users.ts
# CREATE: app/Models/User.ts
最后,你还可以使用-f
标志为模型创建工厂。
node ace make:model User -f
# CREATE: app/Models/User.ts
# CREATE: database/factories/User.ts
make:model
命令在app/Models
目录中创建一个新模型。每个模型都必须扩展BaseModel
类以继承附加功能。
import { DateTime } from 'luxon'
import { BaseModel, column } from '@ioc:Adonis/Lucid/Orm'
export default class User extends BaseModel {
@column({ isPrimary: true })
public id: number
@column.dateTime({ autoCreate: true })
public createdAt: DateTime
@column.dateTime({ autoCreate: true, autoUpdate: true })
public updatedAt: DateTime
}
列
你必须将数据库列定义为类的属性,并使用@column
装饰器来装饰它们。
由于 AdonisJS 使用 TypeScript,如果不在类上显式定义列,就无法绕过。否则,TypeScript 编译器会报出以下错误。
注意事项
@column
装饰器用于区分标准类属性和数据库列。- 我们保持模型精简,不在模型内部定义特定于数据库的约束、数据类型和触发器。
- 你在模型中定义的任何选项都不会更改/影响数据库,你必须为此使用迁移。
总结以上几点 - Lucid 保持迁移和模型之间的明确分离。迁移旨在创建/更改表,模型旨在查询数据库或插入新记录。
定义列
现在假设你已经知道模型类中存在列,以下是将用户表列定义为 User
模型上的属性的示例。
import { DateTime } from 'luxon'
import { BaseModel, column } from '@ioc:Adonis/Lucid/Orm'
export default class User extends BaseModel {
@column({ isPrimary: true })
public id: number
@column()
public username: string
@column()
public email: string
@column({ serializeAs: null })
public password: string
@column()
public avatarUrl: string | null
@column.dateTime({ autoCreate: true })
public createdAt: DateTime
@column.dateTime({ autoCreate: true, autoUpdate: true })
public updatedAt: DateTime
}
@column
装饰器还接受配置属性行为。
isPrimary
选项将属性标记为给定数据库表的主键。serializeAs: null
选项会在你将模型序列化为 JSON 时删除该属性。- 查看所有被
@column
装饰器接受的可用选项 。
日期列
Lucid 进一步增强了日期和日期时间属性,并将数据库驱动程序值转换为 luxon.DateTime 的实例。
你需要做的就是使用 @column.date
或 @column.dateTime
装饰器,Lucid 将处理其余的事情。
@column.date()
public dob: DateTime
@column.dateTime({ autoCreate: true })
public createdAt: DateTime
@column.dateTime({ autoCreate: true, autoUpdate: true })
public updatedAt: DateTime
或者,你可以传递 autoCreate
和 autoUpdate
选项以始终在创建和更新操作期间定义时间戳。 请注意,设置这些选项不会修改数据库表或其触发器。
列名
Lucid 假定你的数据库列名称定义为 snake_case
并在数据库查询期间自动将模型属性转换为蛇形大小写(下划线分隔)。例如:
await User.create({ avatarUrl: 'foo.jpg' })
// 实际执行的查询
// insert into "users" ("avatar_url") values (?)
全局覆盖列名
如果你没有在数据库中使用 snake_case
约定,那么可以自定义 命名策略。
内联覆盖列名
你还可以在 @column
装饰器中显式定义数据库列名,这通常有助于在特定用例中绕过约定。
@column({ columnName: 'user_id', isPrimary: true })
public id: number
模型配置
以下是覆盖常规默认值的配置选项。
主键( primaryKey )
定义自定义主键(默认为 id)。在模型上设置 primaryKey
不会修改数据库。在这里,你只是告诉 Lucid 将 id 视为每一行的唯一值。
class User extends Basemodel {
public static primaryKey = 'email'
}
或者使用 primaryKey
列选项。
class User extends Basemodel {
@column({ isPrimary: true })
public email: string
}
table
定义自定义数据库表名。 默认值 为模型名称的复数和蛇形大小写版本。
export default class User extends BaseModel {
public static table = 'app_users'
}
自分配主键(selfAssignPrimaryKey)
如果你不依赖数据库生成主键,请将此选项设置为 true
。例如,你想将 uuid
分配给新行。
import uuid from 'uuid/v4'
import { BaseModel, beforeCreate } from '@ioc:Adonis/Lucid/Orm'
export default class User extends BaseModel {
public static selfAssignPrimaryKey = true
@column({ isPrimary: true })
public id: string
@beforeCreate()
public static assignUuid(user: User) {
user.id = uuid()
}
}
connection
让模型使用 config/database
文件中定义的自定义数据库连接。
备注:
不要使用此属性在运行时切换连接,此属性仅定义在应用程序的整个生命周期中保持不变的静态连接名称。
export default class User extends BaseModel {
public static connection = 'pg'
}
常见问题
模型会自动创建数据库表吗?
不,我们不会将你的模型与数据库同步。必须使用 [migrations](https://docs.adonisjs.com/guides/database/migrations) 创建/更改表。以下是不使用模型创建数据库模式的一些原因:
1. 从模型生成数据库表意味着在模型中定义所有数据库级别的约束和配置。这给模型增加了不必要的冗余。 2. 不是每个数据库更改都像重命名列一样简单。在某些情况下,你希望在重构期间将数据从一个表迁移到另一个表,而这不能/不应该在模型中表达。
我来自TypeORM,我应该如何定义列类型?
我们不在模型中表达数据库类型。相反,我们遵循**精益模型**的方法 ,并在迁移中保留数据库级别的配置。
我可以将我的模型移动到其他地方吗?
是的。你可以自由地将模型放在任何您想要的地方!如果您的模型位于 `app/Something` 文件夹中,您将使用 `App/Something/ModelName` 来加载您的模型。
延伸阅读
本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。