typeorm vs sequelize vs objection vs bookshelf
Node.js 项目中的 ORM 框架选型指南
typeormsequelizeobjectionbookshelf类似的npm包:

Node.js 项目中的 ORM 框架选型指南

bookshelfobjectionsequelizetypeorm 都是 Node.js 生态中流行的 ORM(对象关系映射)库,用于简化数据库操作。sequelizetypeorm 功能全面,支持多种数据库,适合大型项目。objection 基于 SQL 查询构建器,灵活性高。bookshelf 较老,基于 Knex.js,但维护频率较低。这些工具帮助开发者用 JavaScript 对象操作数据库,避免直接编写 SQL。

npm下载趋势

3 年

GitHub Stars 排名

统计详情

npm包名称
下载量
Stars
大小
Issues
发布时间
License
typeorm3,627,05336,37120.8 MB5203 个月前MIT
sequelize2,624,09830,3492.91 MB1,0122 天前MIT
objection252,7967,351645 kB1281 年前MIT
bookshelf57,1446,364-2386 年前MIT

Node.js ORM 框架深度对比:Bookshelf vs Objection vs Sequelize vs TypeORM

在 Node.js 后端开发中,选择合适的 ORM(对象关系映射)框架直接影响项目的可维护性和开发效率。bookshelfobjectionsequelizetypeorm 是四个主流选择,但它们的设计理念和适用场景差异很大。本文将从实际工程角度,对比它们的核心特性。

⚠️ 维护状态与风险提示

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。

  • 支持装饰器语法,开发体验好。
  • 与 NestJS 等框架集成良好。
// typeorm: 示例代码
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;
  @Column()
  username: string;
}

🗂️ 模型定义方式:配置 vs 装饰器

bookshelf 使用简单的对象扩展。

  • 基于 Knex.js,定义直观。
  • 适合习惯传统 JavaScript 的开发者。
// 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 使用装饰器。

  • TypeScript 友好,类型安全。
  • 代码简洁,元数据丰富。
// typeorm: 模型定义
@Entity('users')
export class User {
  @PrimaryGeneratedColumn()
  id: number;
  @Column()
  username: string;
}

📥 数据查询:链式调用 vs 方法调用

bookshelf 使用链式查询。

  • 基于 Knex.js 风格。
  • 适合习惯 SQL 构建器的开发者。
// bookshelf: 查询数据
User.where({ username: 'alice' }).fetch()
  .then(user => console.log(user));

objection 使用查询构建器。

  • 功能强大,支持复杂查询。
  • 语法接近 Knex.js。
// objection: 查询数据
const user = await User.query().findOne({ username: 'alice' });

sequelize 使用内置方法。

  • 方法丰富,如 findAllfindOne
  • 支持复杂条件对象。
// sequelize: 查询数据
const user = await User.findOne({ where: { username: 'alice' } });

typeorm 使用 Repository 模式。

  • 通过 getRepository 获取操作对象。
  • 支持查询构建器。
// typeorm: 查询数据
const user = await userRepository.findOne({ where: { username: 'alice' } });

🔗 关系处理:显式定义 vs 自动推断

bookshelf 需要显式定义关系。

  • 在模型中定义 hasManybelongsTo
  • 查询时使用 withRelated
// bookshelf: 关系查询
User.where({ id: 1 }).fetch({ withRelated: ['posts'] })
  .then(user => console.log(user.related('posts')));

objection 使用 relationMappings

  • 定义清晰,支持 eager loading。
  • 查询时使用 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 查询。

  • 通过 createQueryBuilderfind 选项。
  • 类型安全,自动补全。
// typeorm: 关系查询
const user = await userRepository.findOne({
  where: { id: 1 },
  relations: ['posts']
});

🔄 迁移管理:内置工具 vs 外部库

bookshelf 无内置迁移工具。

  • 需配合 Knex.js 迁移功能。
  • 配置稍显繁琐。
// bookshelf: 迁移(使用 Knex)
// knex migrate:make create_users_table

objection 无内置迁移工具。

  • 推荐配合 Knex.js。
  • 灵活性高,但需手动配置。
// 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

🌐 数据库支持:广泛 vs 特定

bookshelf 支持 Knex.js 支持的数据库。

  • 包括 MySQL、PostgreSQL、SQLite 等。
  • 依赖 Knex.js 的驱动。
// bookshelf: 数据库配置
const knex = require('knex')({ client: 'mysql', connection: { ... } });

objection 支持 Knex.js 支持的数据库。

  • 与 Bookshelf 类似,依赖 Knex。
  • 配置灵活。
// objection: 数据库配置
const knex = require('knex')({ client: 'postgresql', connection: { ... } });

sequelize 原生支持多种数据库。

  • MySQL、PostgreSQL、SQLite、MSSQL、MariaDB。
  • 无需额外配置构建器。
// sequelize: 数据库配置
const sequelize = new Sequelize('database', 'username', 'password', {
  dialect: 'postgres'
});

typeorm 支持多种数据库。

  • MySQL、PostgreSQL、SQLite、Oracle、MongoDB 等。
  • 驱动丰富,配置统一。
// typeorm: 数据库配置
const dataSource = new DataSource({
  type: 'postgres',
  host: 'localhost',
  // ...
});

📊 总结对比表

特性bookshelfobjectionsequelizetypeorm
维护状态⚠️ 低频维护✅ 活跃✅ 稳定✅ 活跃
模型定义对象扩展类继承类/初始化装饰器
查询风格链式调用查询构建器方法调用Repository
TypeScript❌ 支持一般✅ 支持良好✅ 支持良好✅ 原生支持
迁移工具需 Knex需 Knex内置 CLI内置 CLI
数据库Knex 支持Knex 支持多种原生多种原生

💡 选型建议

bookshelf 适合旧项目维护,新项目不推荐。

  • 如果你正在维护一个基于 Bookshelf 的旧系统,可以继续使用该框架。
  • 但如果是新项目,建议考虑其他更活跃的选项。

objection 适合需要灵活查询的场景。

  • 如果你的项目需要复杂的 SQL 查询,Objection 是很好的选择。
  • 它基于 Knex.js,既保留了 SQL 的灵活性,又提供了 ORM 的便利。

sequelize 适合企业级稳定项目。

  • 如果你需要一个经过时间考验、文档完善的 ORM,Sequelize 是安全的选择。
  • 它支持多种数据库,社区庞大,遇到问题容易找到解决方案。

typeorm 适合 TypeScript 项目和现代架构。

  • 如果你使用 TypeScript 或 NestJS,TypeORM 是最佳选择。
  • 它的装饰器语法和 Repository 模式让代码更简洁、类型更安全。

🏁 最终结论

这四个框架各有优劣,选择的关键在于你的项目需求和技术栈。

  • 追求稳定和生态:选 sequelize
  • 追求 TypeScript 体验:选 typeorm
  • 追求查询灵活性:选 objection
  • 旧项目维护:可继续用 bookshelf,但新项目避免使用。

无论选择哪个,都要注意数据库设计的合理性,ORM 只是工具,良好的架构才是核心。

如何选择: typeorm vs sequelize vs objection vs bookshelf

  • typeorm:

    选择 typeorm 如果你使用 TypeScript,或者喜欢装饰器语法。它支持 Active Record 和 Data Mapper 模式,适合现代 Node.js 项目。与 NestJS 等框架集成良好,适合追求开发体验的团队。

  • sequelize:

    选择 sequelize 如果你需要稳定的、功能丰富的 ORM,支持多种数据库(MySQL、PostgreSQL 等)。它适合企业级应用,文档完善,社区庞大。如果你重视长期支持和稳定性,这是安全的选择。

  • objection:

    选择 objection 如果你需要灵活的 SQL 查询能力,同时想要 ORM 的便利。它基于 Knex.js,适合复杂查询场景。社区维护良好,文档清晰,适合中大型项目。

  • bookshelf:

    如果你的项目依赖旧版代码库且已稳定运行,可以继续使用 bookshelf。但在新项目中不建议选择,因为其社区活跃度下降,新功能支持有限。它基于 Knex.js,适合喜欢 SQL 查询构建器风格的开发者。

typeorm的README

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.

Features

  • Supports both DataMapper and ActiveRecord (your choice).
  • Entities and columns.
  • Database-specific column types.
  • Entity manager.
  • Repositories and custom repositories.
  • Clean object-relational model.
  • Associations (relations).
  • Eager and lazy relations.
  • Unidirectional, bidirectional, and self-referenced relations.
  • Supports multiple inheritance patterns.
  • Cascades.
  • Indices.
  • Transactions.
  • Migrations and automatic migrations generation.
  • Connection pooling.
  • Replication.
  • Using multiple database instances.
  • Working with multiple database types.
  • Cross-database and cross-schema queries.
  • Elegant-syntax, flexible and powerful QueryBuilder.
  • Left and inner joins.
  • Proper pagination for queries using joins.
  • Query caching.
  • Streaming raw results.
  • Logging.
  • Listeners and subscribers (hooks).
  • Supports closure table pattern.
  • Schema declaration in models or separate configuration files.
  • Supports MySQL / MariaDB / Postgres / CockroachDB / SQLite / Microsoft SQL Server / Oracle / SAP Hana / sql.js.
  • Supports MongoDB NoSQL database.
  • Works in Node.js / Browser / Ionic / Cordova / React Native / NativeScript / Expo / Electron platforms.
  • TypeScript and JavaScript support.
  • ESM and CommonJS support.
  • Produced code is performant, flexible, clean, and maintainable.
  • Follows all possible best practices.
  • CLI.

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()

Samples

Take a look at the samples in sample for examples of usage.

There are a few repositories that you can clone and start with:

Extensions

There are several extensions that simplify working with TypeORM and integrating it with other modules:

Contributing

Learn about contribution here and how to set up your development environment here.

This project exists thanks to all the people who contribute:

Sponsors

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.

Champion

Become a champion sponsor and get premium technical support from our core contributors. Become a champion

Supporter

Support TypeORM's development with a monthly contribution. Become a supporter

Community

Join our community of supporters and help sustain TypeORM. Become a community supporter

Sponsor

Make a one-time or recurring contribution of your choice. Become a sponsor