WatermelonDB 数据模型(Model)深度解析
什么是数据模型(Model)
在 WatermelonDB 中,Model 类代表着应用程序中的一种实体类型。例如,在博客应用中常见的 Post
(文章)、Comment
(评论)、User
(用户) 等都是典型的数据模型。每个 Model 都对应着数据库中的一个表,它定义了该类型实体的结构和行为。
创建基础模型
让我们从创建一个简单的 Post
模型开始:
// model/Post.js
import { Model } from '@nozbe/watermelondb'
export default class Post extends Model {
static table = 'posts'
}
关键点说明:
- 每个 Model 必须继承自基础
Model
类 - 必须通过
static table
指定对应的表名(与之前定义的 Schema 保持一致) - 创建后需要将 Model 注册到数据库实例中
// index.js
import Post from 'model/Post'
const database = new Database({
modelClasses: [Post], // 在此处注册模型
// ...其他配置
})
模型关联关系
现实应用中的数据很少孤立存在,模型之间通常存在各种关联关系。WatermelonDB 提供了清晰的关联定义方式。
一对多关系示例
假设一个文章(Post)可以有多条评论(Comment),每条评论属于一篇文章:
class Post extends Model {
static table = 'posts'
static associations = {
comments: { type: 'has_many', foreignKey: 'post_id' },
}
}
class Comment extends Model {
static table = 'comments'
static associations = {
post: { type: 'belongs_to', key: 'post_id' },
}
}
关键点:
- 在"父"模型(Post)中使用
has_many
类型 - 在"子"模型(Comment)中使用
belongs_to
类型 - 必须使用相同的外键字段名(
post_id
) foreignKey
和key
参数实际上指向同一字段,只是命名约定不同
定义模型字段
模型字段对应数据库表中的列,使用装饰器语法定义:
import { field, text } from '@nozbe/watermelondb/decorators'
class Post extends Model {
static table = 'posts'
@text('title') title // 文本字段,自动去除首尾空格
@text('body') body // 同上
@field('is_pinned') isPinned // 普通字段
}
字段类型说明:
@field
- 基础字段装饰器,类型与 Schema 中定义的列类型一致@text
- 专为用户输入的文本设计,会自动处理空格- 字段名使用 camelCase(JavaScript 惯例),列名使用 snake_case(数据库惯例)
日期字段处理
对于日期类型,使用专门的 @date
装饰器:
import { date } from '@nozbe/watermelondb/decorators'
class Post extends Model {
@date('last_event_at') lastEventAt // 返回 JavaScript Date 对象
}
计算字段
可以通过 getter 定义基于现有字段的计算属性:
class Post extends Model {
@date('archived_at') archivedAt
get isRecentlyArchived() {
return this.archivedAt &&
this.archivedAt.getTime() > Date.now() - 7 * 24 * 3600 * 1000
}
}
高级关联定义
一对一关系
使用 @relation
或 @immutableRelation
定义指向单个相关记录的字段:
import { relation, immutableRelation } from '@nozbe/watermelondb/decorators'
class Comment extends Model {
@relation('posts', 'post_id') post // 可变的关联文章
@immutableRelation('users', 'author_id') author // 不可变的作者
}
区别:
@relation
- 常规关联,可以修改@immutableRelation
- 不可变关联,创建后不能修改
一对多关系查询
使用 @children
装饰器定义获取子记录的查询:
import { children } from '@nozbe/watermelondb/decorators'
class Post extends Model {
static associations = {
comments: { type: 'has_many', foreignKey: 'post_id' },
}
@children('comments') comments // 获取所有关联评论
}
注意:必须先定义 has_many
关联才能使用 @children
自定义查询
可以基于基础查询创建更复杂的自定义查询:
import { Q } from '@nozbe/watermelondb'
class Post extends Model {
@children('comments') comments
@lazy verifiedComments = this.comments.extend(
Q.where('is_verified', true) // 只获取已验证的评论
)
}
数据操作方法
定义写入方法
使用 @writer
装饰器定义修改数据的方法:
import { writer } from '@nozbe/watermelondb/decorators'
class Comment extends Model {
@field('is_spam') isSpam
@writer async markAsSpam() {
await this.update(comment => {
comment.isSpam = true // 标记为垃圾评论
})
}
}
关键点:
- 所有修改数据的操作必须标记为
@writer
- 使用
update
方法进行记录更新
高级字段特性
WatermelonDB 还提供了一些高级字段装饰器:
@json
- 处理复杂 JSON 数据@readonly
- 创建只读字段@nochange
- 字段创建后不可修改- 使用 RxJS 创建可观察的复合属性
最佳实践建议
- 命名一致性:保持模型类名(大驼峰)与表名(蛇形)的明确对应关系
- 关联清晰:明确定义双向关联,保持外键一致
- 类型匹配:确保装饰器类型与 Schema 中定义的列类型匹配
- 业务封装:将常用数据操作封装为模型方法
- 性能考虑:对于复杂查询,考虑添加适当索引
通过合理设计数据模型,可以构建出结构清晰、易于维护的应用程序数据层。WatermelonDB 的 Model 系统提供了强大的工具来定义数据结构和关系,同时保持了良好的开发体验。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考