* docs: add hooks dev doc * docs: change name from hooks to events
17 KiB
Database
NocoBase 内置的数据库访问类,通过封装 Sequelize 提供了更加简单的数据库访问接口和统一化的 JSON 数据库表配置方式,同时也提供了扩展字段类型和查询操作符的能力。
Database 类继承自 EventEmitter,可以通过 db.on('event', callback)
监听数据库事件,以及 db.off('event', callback)
移除监听。
包结构
可通过以下方式引入相关实体:
import Database, {
Field,
Collection,
Repository,
RelationRepository,
extend
} from '@nocobase/database';
构造函数
签名
constructor(options: DatabaseOptions)
创建一个数据库实例。
参数
options
参数与 Sequelize 的构造参数一致的部分会透传至 Sequelize,同时 NocoBase 也会使用一些额外的参数:
参数名 | 类型 | 默认值 | 描述 |
---|---|---|---|
options.host |
string |
'localhost' |
数据库主机 |
options.port |
number |
- | 数据库服务端口,根据使用的数据库有对应默认端口 |
options.username |
string |
- | 数据库用户名 |
options.password |
string |
- | 数据库密码 |
options.database |
string |
- | 数据库名称 |
options.dialect |
string |
'mysql' |
数据库类型 |
options.storage? |
string |
':memory:' |
SQLite 的存储模式 |
options.logging? |
boolean |
false |
是否开启日志 |
options.define? |
Object |
{} |
默认的表定义参数 |
options.tablePrefix? |
string |
'' |
NocoBase 扩展,表名前缀 |
options.migrator? |
UmzugOptions |
{} |
NocoBase 扩展,迁移管理器相关参数,参考 Umzug 实现 |
示例
import Database from '@nocobase/database';
const app = new Database({
dialect: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: '123456',
database: 'test',
tablePrefix: 'my_'
});
实例成员
sequelize
初始化后的 Sequelize 实例,在需要使用 sequelize 底层方法时可以调用,相关信息可以直接参考 sequelize 的文档。
options
初始化的配置参数,包含了 Sequelize 的配置参数和 NocoBase 的额外配置参数。
version
连接的数据库的版本信息对象,可通过 await db.version.satisfies(<sem_ver>)
检查是否满足特定数据库版本要求。
示例
const r = await this.db.version.satisfies({
mysql: '>=8.0.17',
sqlite: '3.x',
postgres: '>=10',
});
迁移相关方法
addMigration()
添加单个迁移文件。
签名
addMigration(options: MigrationItem)
参数
参数名 | 类型 | 默认值 | 描述 |
---|---|---|---|
options.name |
string |
- | 迁移文件名称 |
options.context? |
string |
- | 迁移文件的 up 方法 |
options.migration? |
typeof Migration |
- | 迁移文件的自定义类 |
options.up |
Function |
- | 迁移文件的 up 方法 |
options.down |
Function |
- | 迁移文件的 down 方法 |
示例
db.addMigration({
name: '20220916120411-test-1',
async up() {
const queryInterface = this.context.db.sequelize.getQueryInterface();
await queryInterface.query(/* your migration sqls */);
}
});
addMigrations()
添加指定目录下的迁移文件。
签名
addMigrations(options: AddMigrationsOptions): void
参数
参数名 | 类型 | 默认值 | 描述 |
---|---|---|---|
options.directory |
string |
'' |
迁移文件所在目录 |
options.extensions |
string[] |
['js', 'ts'] |
文件扩展名 |
options.namespace? |
string |
'' |
命名空间 |
options.context? |
Object |
{ db } |
迁移文件的上下文 |
示例
db.addMigrations({
directory: path.resolve(__dirname, './migrations'),
namespace: 'test'
});
工具方法
inDialect()
判断当前数据库类型是否为指定类型。
签名
inDialect(dialect: string[]): boolean
参数
参数名 | 类型 | 默认值 | 描述 |
---|---|---|---|
dialect |
string[] |
- | 数据库类型,可选值为 mysql /postgres /sqlite |
getTablePrefix()
获取配置中的表名前缀。
签名
getTablePrefix(): string
数据表配置
collection()
定义一个数据表。该调用类似与 Sequelize 的 define
方法,只在内存中创建表结构,如需持久化到数据库,需要调用 sync
方法。
签名
collection(options: CollectionOptions): Collection
参数
options
所有配置参数与 Collection
类的构造函数一致,参考 Collection。
事件
'beforeDefineCollection'
:在定义表之前触发。'afterDefineCollection'
:在定义表之后触发。
示例
db.collection({
name: 'books',
fields: [
{
type: 'string',
name: 'title',
},
{
type: 'float',
name: 'price',
}
]
});
// sync collection as table to db
await db.sync();
getCollection()
获取已定义的数据表。
签名
getCollection(name: string): Collection
参数
参数名 | 类型 | 默认值 | 描述 |
---|---|---|---|
name |
string |
- | 表名 |
示例
const collection = db.getCollection('books');
hasCollection()
判断是否已定义指定的数据表。
签名
hasCollection(name: string): boolean
参数
参数名 | 类型 | 默认值 | 描述 |
---|---|---|---|
name |
string |
- | 表名 |
示例
db.collection({ name: 'books' });
db.hasCollection('books'); // true
db.hasCollection('authors'); // false
removeCollection()
移除已定义的数据表。仅在内存中移除,如需持久化,需要调用 sync
方法。
签名
removeCollection(name: string): void
参数
参数名 | 类型 | 默认值 | 描述 |
---|---|---|---|
name |
string |
- | 表名 |
事件
'beforeRemoveCollection'
:在移除表之前触发。'afterRemoveCollection'
:在移除表之后触发。
示例
db.collection({ name: 'books' });
db.removeCollection('books');
import()
导入文件目录下所有文件作为 collection 配置载入内存。
签名
async import(options: { directory: string; extensions?: ImportFileExtension[] }): Promise<Map<string, Collection>>
参数
参数名 | 类型 | 默认值 | 描述 |
---|---|---|---|
options.directory |
string |
- | 要导入的目录路径 |
options.extensions |
string[] |
['ts', 'js'] |
扫描特定后缀 |
示例
./collections/books.ts
文件定义的 collection 如下:
export default {
name: 'books',
fields: [
{
type: 'string',
name: 'title',
}
]
};
在插件加载时导入相关配置:
class Plugin {
async load() {
await this.app.db.import({
directory: path.resolve(__dirname, './collections'),
});
}
}
扩展注册与获取
registerFieldTypes()
注册自定义字段类型。
签名
registerFieldTypes(fieldTypes: MapOf<typeof Field>): void
参数
fieldTypes
是一个键值对,键为字段类型名称,值为字段类型类。
示例
import { Field } from '@nocobase/database';
class MyField extends Field {
// ...
}
db.registerFieldTypes({
myField: MyField,
});
registerModels()
注册自定义数据模型类。
签名
registerModels(models: MapOf<ModelCtor<any>>): void
参数
models
是一个键值对,键为数据模型名称,值为数据模型类。
示例
import { Model } from '@nocobase/database';
class MyModel extends Model {
// ...
}
db.registerModels({
myModel: MyModel,
});
db.collection({
name: 'myCollection',
model: 'myModel'
});
registerRepositories()
注册自定义数据仓库类。
签名
registerRepositories(repositories: MapOf<RepositoryType>): void
参数
repositories
是一个键值对,键为数据仓库名称,值为数据仓库类。
示例
import { Repository } from '@nocobase/database';
class MyRepository extends Repository {
// ...
}
db.registerRepositories({
myRepository: MyRepository,
});
db.collection({
name: 'myCollection',
repository: 'myRepository'
});
registerOperators()
注册自定义数据查询操作符。
签名
registerOperators(operators: MapOf<OperatorFunc>)
参数
operators
是一个键值对,键为操作符名称,值为操作符比较语句生成函数。
示例
db.registerOperators({
$dateOn(value) {
return {
[Op.and]: [{ [Op.gte]: stringToDate(value) }, { [Op.lt]: getNextDay(value) }],
};
}
});
db.getRepository('books').count({
filter: {
createdAt: {
// registered operator
$dateOn: '2020-01-01',
}
}
});
getModel()
获取已定义的数据模型类。如果没有在之前注册自定义模型类,将返回 Sequelize 默认的模型类。默认名称与 collection 定义的名称相同。
签名
getModel(name: string): Model
参数
参数名 | 类型 | 默认值 | 描述 |
---|---|---|---|
name |
string |
- | 已注册的模型名 |
示例
db.registerModels({
books: class MyModel extends Model {}
});
const ModelClass = db.getModel('books');
console.log(ModelClass.prototype instanceof MyModel) // true
注:从 collection 中获取的模型类并不与注册时的模型类严格相等,而是继承自注册时的模型类。由于 Sequelize 的模型类在初始化过程中属性会被修改,所以 NocoBase 自动处理了这个继承关系。除类不相等以外,其他所有定义都可以正常使用。
getRepository()
获取自定义的数据仓库类。如果没有在之前注册自定义数据仓库类,将返回 NocoBase 默认的数据仓库类。默认名称与 collection 定义的名称相同。
数据仓库类主要用于基于数据模型的增删改查等操作,参考 数据仓库。
签名
getRepository(name: string): Repository
getRepository(name: string, relationId?: string | number): Repository
参数
参数名 | 类型 | 默认值 | 描述 |
---|---|---|---|
name |
string |
- | 已注册的数据仓库名 |
relationId |
string | number |
- | 关系数据的外键值 |
当名称是形如 'tables.relactions'
的带关联的名称时,将返回关联的数据仓库类。如果提供了第二个参数,数据仓库在使用时(查询、修改等)会基于关系数据的外键值。
示例
假设有两张数据表_文章_与_作者_,并且文章表中有一个外键指向作者表:
const AuthorsRepo = db.getRepository('authors');
const author1 = AuthorsRepo.create({ name: 'author1' });
const PostsRepo = db.getRepository('authors.posts', author1.id);
const post1 = AuthorsRepo.create({ title: 'post1' });
asset(post1.authorId === author1.id); // true
数据库事件
on()
监听数据库事件。
Database 除了对 sequelize 原生的事件封装以外,还提供以下可监听事件类型:
事件名称 | 是否异步 | 描述 |
---|---|---|
'beforeDefineCollection' |
否 | 定义 collection 之前触发 |
'afterDefineCollection' |
否 | 定义 collection 之后触发 |
'beforeRemoveCollection' |
否 | 移除 collection 之前触发 |
'afterRemoveCollection' |
否 | 移除 collection 之后触发 |
<sequelize_model_global_event> |
- | 所有 sequelize 的全局事件均可通过此方式监听,详见示例部分,是否异步根据具体事件 |
<model_name>.<sequelize_model_event> |
是 | 所有 sequelize model 的事件均可通过此方式监听,详见示例部分 |
<model_name>.afterCreateWithAssociations |
是 | NocoBase 扩展的当连同关联数据一并创建记录成功后触发的事件(使用 Repository 的 create 方法会触发) |
<model_name>.afterUpdateWithAssociations |
是 | NocoBase 扩展的当连同关联数据一并更新记录成功后触发的事件(使用 Repository 的 update 方法会触发) |
<model_name>.afterSaveWithAssociations |
是 | NocoBase 扩展的当连同关联数据一并创建或更新记录成功后触发的事件(使用 Repository 的 create/update 方法都会触发) |
其中 <model_name>.afterXXXWithAssociations
事件只有在使用 Repository 的实例方法时才会被触发,所以建议大部分时候都使用 Repository 来进行数据操作。
签名
on(event: string, listener: (...args: any[]) => void | Promise<void>): void
参数
参数名 | 类型 | 默认值 | 描述 |
---|---|---|---|
event | string | - | 事件名称 |
listener | Function | - | 事件监听器 |
示例
监听 books 表事件,当创建新记录成功后触发:
db.on('books.afterCreate', async (model, options) => {
const { transaction } = options;
const result = await model.constructor.findByPk(model.id, {
transaction
});
console.log(result);
});
监听 books 表,当连同关联数据一并创建成功后触发的事件:
db.on('books.afterCreateWithAssociations', async (model, options) => {
const { transaction } = options;
const result = await model.constructor.findByPk(model.id, {
transaction
});
console.log(result);
});
监听任意表的删除事件:
db.on('afterDestroy', async (model, options) => {
console.log(model);
});
off()
移除事件监听函数。
签名
off(name: string, listener: Function)
参数名 | 类型 | 默认值 | 描述 |
---|---|---|---|
name | string | - | 事件名称 |
listener | Function | - | 事件监听器 |
示例
const listener = async (model, options) => {
console.log(model);
};
db.on('afterCreate', listener);
db.off('afterCreate', listener);
数据库操作
auth()
数据库连接验证。可以用于确保应用与数据已建立连接。
签名
auth(options: QueryOptions & { retry?: number } = {}): Promise<boolean>
参数
参数名 | 类型 | 默认值 | 描述 |
---|---|---|---|
options? |
Object |
- | 验证选项 |
options.retry? |
number |
10 |
验证失败时重试次数 |
options.transaction? |
Transaction |
- | 事务对象 |
options.logging? |
boolean | Function |
false |
是否打印日志 |
示例
await db.auth();
reconnect()
重新连接数据库。
示例
await db.reconnect();
closed()
判断数据库是否已关闭连接。
签名
closed(): boolean
close()
关闭数据库连接。等同于 sequelize.close()
。
sync()
同步数据库表结构。等同于 sequelize.sync()
,参数参考 Sequelize 文档。
clean()
清空数据库,将删除所有数据表。
签名
clean(options: CleanOptions): Promise<void>
参数
参数名 | 类型 | 默认值 | 描述 |
---|---|---|---|
options.drop |
boolean |
false |
是否移除所有数据表 |
options.skip |
string[] |
- | 跳过的表名配置 |
options.transaction |
Transaction |
- | 事务对象 |
示例
移除除 users
表以外的所有表。
await db.clean({
drop: true,
skip: ['users']
})
包级导出
defineCollection()
创建一个数据表的配置内容。
签名
defineCollection(name: string, config: CollectionOptions): CollectionOptions
参数
参数名 | 类型 | 默认值 | 描述 |
---|---|---|---|
collectionOptions |
CollectionOptions |
- | 与所有 db.collection() 的参数相同 |
示例
对于要被 db.import()
导入的数据表配置文件:
import { defineCollection } from '@nocobase/database';
export default defineCollection({
name: 'users',
fields: [
{
type: 'string',
name: 'name',
},
],
});
extendCollection()
扩展已在内存中的表结构配置内容,主要用于 import()
方法导入的文件内容。该方法是 @nocobase/database
包导出的顶级方法,不通过 db 实例调用。也可以使用 extend
别名。
签名
extendCollection(collectionOptions: CollectionOptions, mergeOptions?: MergeOptions): ExtendedCollectionOptions
参数
参数名 | 类型 | 默认值 | 描述 |
---|---|---|---|
collectionOptions |
CollectionOptions |
- | 与所有 db.collection() 的参数相同 |
mergeOptions? |
MergeOptions |
- | npm 包 deepmerge 的参数 |
示例
原始 books 表定义(books.ts):
export default {
name: 'books',
fields: [
{ name: 'title', type: 'string' }
]
}
扩展 books 表定义(books.extend.ts):
import { extend } from '@nocobase/database';
// 再次扩展
export default extend({
name: 'books',
fields: [
{ name: 'price', type: 'number' }
]
});
以上两个文件如在调用 import()
时导入,通过 extend()
再次扩展以后,books 表将拥有 title
和 price
两个字段。
此方法在扩展已有插件已定义的表结构时非常有用。