bookshelf、objection、sequelize 和 typeorm 都是 Node.js 生态中流行的 ORM(对象关系映射)库,用于简化数据库操作。sequelize 和 typeorm 功能全面,支持多种数据库,适合大型项目。objection 基于 SQL 查询构建器,灵活性高。bookshelf 较老,基于 Knex.js,但维护频率较低。这些工具帮助开发者用 JavaScript 对象操作数据库,避免直接编写 SQL。
在 Node.js 后端开发中,选择合适的 ORM(对象关系映射)框架直接影响项目的可维护性和开发效率。bookshelf、objection、sequelize 和 typeorm 是四个主流选择,但它们的设计理念和适用场景差异很大。本文将从实际工程角度,对比它们的核心特性。
bookshelf 目前处于维护模式,更新频率较低。
// bookshelf: 示例代码(注意:社区支持减少)
const Bookshelf = require('bookshelf')(knex);
const User = Bookshelf.Model.extend({ tableName: 'users' });
objection 维护良好,基于 Knex.js。
// objection: 示例代码
const { Model } = require('objection');
class User extends Model {
static get tableName() {
return 'users';
}
}
sequelize 稳定且功能丰富。
// sequelize: 示例代码
const { Sequelize, Model } = require('sequelize');
class User extends Model {}
User.init({ username: Sequelize.STRING }, { sequelize, tableName: 'users' });
typeorm 现代且支持 TypeScript。
// typeorm: 示例代码
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
username: string;
}
bookshelf 使用简单的对象扩展。
// bookshelf: 模型定义
const User = Bookshelf.Model.extend({
tableName: 'users',
hasTimestamps: true
});
objection 使用类继承。
// objection: 模型定义
class User extends Model {
static get tableName() {
return 'users';
}
static get relationMappings() {
return { posts: { model: Post, relation: Model.HasManyRelation } };
}
}
sequelize 使用初始化方法或类定义。
// sequelize: 模型定义
class User extends Model {}
User.init({
username: DataTypes.STRING
}, {
sequelize,
tableName: 'users'
});
typeorm 使用装饰器。
// typeorm: 模型定义
@Entity('users')
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
username: string;
}
bookshelf 使用链式查询。
// bookshelf: 查询数据
User.where({ username: 'alice' }).fetch()
.then(user => console.log(user));
objection 使用查询构建器。
// objection: 查询数据
const user = await User.query().findOne({ username: 'alice' });
sequelize 使用内置方法。
findAll、findOne。// sequelize: 查询数据
const user = await User.findOne({ where: { username: 'alice' } });
typeorm 使用 Repository 模式。
getRepository 获取操作对象。// typeorm: 查询数据
const user = await userRepository.findOne({ where: { username: 'alice' } });
bookshelf 需要显式定义关系。
hasMany、belongsTo。withRelated。// bookshelf: 关系查询
User.where({ id: 1 }).fetch({ withRelated: ['posts'] })
.then(user => console.log(user.related('posts')));
objection 使用 relationMappings。
withGraphFetched。// objection: 关系查询
const user = await User.query()
.findById(1)
.withGraphFetched('posts');
sequelize 使用 include 选项。
// sequelize: 关系查询
const user = await User.findOne({
where: { id: 1 },
include: [{ model: Post }]
});
typeorm 使用 Join 查询。
createQueryBuilder 或 find 选项。// typeorm: 关系查询
const user = await userRepository.findOne({
where: { id: 1 },
relations: ['posts']
});
bookshelf 无内置迁移工具。
// bookshelf: 迁移(使用 Knex)
// knex migrate:make create_users_table
objection 无内置迁移工具。
// objection: 迁移(使用 Knex)
// knex migrate:make create_users_table
sequelize 内置 CLI 迁移工具。
sequelize-cli。// sequelize: 迁移命令
// npx sequelize-cli migration:generate --name create-users
typeorm 内置迁移工具。
// typeorm: 迁移命令
// npm run typeorm -- migration:generate -- -n CreateUsers
bookshelf 支持 Knex.js 支持的数据库。
// bookshelf: 数据库配置
const knex = require('knex')({ client: 'mysql', connection: { ... } });
objection 支持 Knex.js 支持的数据库。
// objection: 数据库配置
const knex = require('knex')({ client: 'postgresql', connection: { ... } });
sequelize 原生支持多种数据库。
// sequelize: 数据库配置
const sequelize = new Sequelize('database', 'username', 'password', {
dialect: 'postgres'
});
typeorm 支持多种数据库。
// typeorm: 数据库配置
const dataSource = new DataSource({
type: 'postgres',
host: 'localhost',
// ...
});
| 特性 | bookshelf | objection | sequelize | typeorm |
|---|---|---|---|---|
| 维护状态 | ⚠️ 低频维护 | ✅ 活跃 | ✅ 稳定 | ✅ 活跃 |
| 模型定义 | 对象扩展 | 类继承 | 类/初始化 | 装饰器 |
| 查询风格 | 链式调用 | 查询构建器 | 方法调用 | Repository |
| TypeScript | ❌ 支持一般 | ✅ 支持良好 | ✅ 支持良好 | ✅ 原生支持 |
| 迁移工具 | 需 Knex | 需 Knex | 内置 CLI | 内置 CLI |
| 数据库 | Knex 支持 | Knex 支持 | 多种原生 | 多种原生 |
bookshelf 适合旧项目维护,新项目不推荐。
objection 适合需要灵活查询的场景。
sequelize 适合企业级稳定项目。
typeorm 适合 TypeScript 项目和现代架构。
这四个框架各有优劣,选择的关键在于你的项目需求和技术栈。
sequelize。typeorm。objection。bookshelf,但新项目避免使用。无论选择哪个,都要注意数据库设计的合理性,ORM 只是工具,良好的架构才是核心。
选择 typeorm 如果你使用 TypeScript,或者喜欢装饰器语法。它支持 Active Record 和 Data Mapper 模式,适合现代 Node.js 项目。与 NestJS 等框架集成良好,适合追求开发体验的团队。
选择 sequelize 如果你需要稳定的、功能丰富的 ORM,支持多种数据库(MySQL、PostgreSQL 等)。它适合企业级应用,文档完善,社区庞大。如果你重视长期支持和稳定性,这是安全的选择。
选择 objection 如果你需要灵活的 SQL 查询能力,同时想要 ORM 的便利。它基于 Knex.js,适合复杂查询场景。社区维护良好,文档清晰,适合中大型项目。
如果你的项目依赖旧版代码库且已稳定运行,可以继续使用 bookshelf。但在新项目中不建议选择,因为其社区活跃度下降,新功能支持有限。它基于 Knex.js,适合喜欢 SQL 查询构建器风格的开发者。
TypeORM is an ORM that can run in Node.js, Browser, Cordova, Ionic, React Native, NativeScript, Expo, and Electron platforms and can be used with TypeScript and JavaScript (ES2021). Its goal is to always support the latest JavaScript features and provide additional features that help you to develop any kind of application that uses databases - from small applications with a few tables to large-scale enterprise applications with multiple databases.
TypeORM supports more databases than any other JS/TS ORM: Google Spanner, Microsoft SqlServer, MySQL/MariaDB, MongoDB, Oracle, Postgres, SAP HANA and SQLite, as well we derived databases and different drivers.
TypeORM supports both Active Record and Data Mapper patterns, unlike all other JavaScript ORMs currently in existence, which means you can write high-quality, loosely coupled, scalable, maintainable applications in the most productive way.
TypeORM is highly influenced by other ORMs, such as Hibernate, Doctrine and Entity Framework.
And more...
With TypeORM, your models look like this:
import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number
@Column()
firstName: string
@Column()
lastName: string
@Column()
age: number
}
And your domain logic looks like this:
const userRepository = MyDataSource.getRepository(User)
const user = new User()
user.firstName = "Timber"
user.lastName = "Saw"
user.age = 25
await userRepository.save(user)
const allUsers = await userRepository.find()
const firstUser = await userRepository.findOneBy({
id: 1,
}) // find by id
const timber = await userRepository.findOneBy({
firstName: "Timber",
lastName: "Saw",
}) // find by firstName and lastName
await userRepository.remove(timber)
Alternatively, if you prefer to use the ActiveRecord implementation, you can use it as well:
import { Entity, PrimaryGeneratedColumn, Column, BaseEntity } from "typeorm"
@Entity()
export class User extends BaseEntity {
@PrimaryGeneratedColumn()
id: number
@Column()
firstName: string
@Column()
lastName: string
@Column()
age: number
}
And your domain logic will look this way:
const user = new User()
user.firstName = "Timber"
user.lastName = "Saw"
user.age = 25
await user.save()
const allUsers = await User.find()
const firstUser = await User.findOneBy({
id: 1,
})
const timber = await User.findOneBy({
firstName: "Timber",
lastName: "Saw",
})
await timber.remove()
Take a look at the samples in sample for examples of usage.
There are a few repositories that you can clone and start with:
There are several extensions that simplify working with TypeORM and integrating it with other modules:
data-source.ts after generating migrations/entities - typeorm-codebase-syncrelations objects - typeorm-relationsrelations based on a GraphQL query - typeorm-relations-graphqlLearn about contribution here and how to set up your development environment here.
This project exists thanks to all the people who contribute:
Open source is hard and time-consuming. If you want to invest in TypeORM's future, you can become a sponsor and allow our core team to spend more time on TypeORM's improvements and new features.
Become a champion sponsor and get premium technical support from our core contributors. Become a champion
Support TypeORM's development with a monthly contribution. Become a supporter
Join our community of supporters and help sustain TypeORM. Become a community supporter
Make a one-time or recurring contribution of your choice. Become a sponsor