# bun-orm **Repository Path**: iamhefang/bun-orm ## Basic Information - **Project Name**: bun-orm - **Description**: No description available - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-05-18 - **Last Updated**: 2026-05-18 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # bun-orm Bun 原生的 ORM 库,零依赖,使用 `Bun.SQL` 作为统一数据库驱动,支持 SQLite / MySQL / PostgreSQL。提供 Builder 链式查询和 TypeORM 风格装饰器两种使用方式。 ## 安装 ```bash bun add bun-orm ``` 无需安装任何其他依赖。 ## 快速开始 ### Builder 风格 ```ts import { Orm } from "bun-orm"; const orm = await Orm.connect({ filename: ":memory:" }); // 建表 await orm.schema.createTable("users", (table) => { table.increments("id"); table.string("name", 100).notNullable(); table.string("email").unique(); table.integer("age").default(0); table.timestamps(); }); // 插入 const [user] = await orm.query("users") .insert({ name: "Alice", email: "alice@test.com", age: 30 }) .returning("id", "name", "email"); // 查询 const users = await orm.query("users") .select("id", "name", "email") .where("age", ">", 20) .orderBy("name", "asc") .limit(10); // 更新 await orm.query("users").where("id", user.id).update({ name: "Alice Updated" }); // 删除 await orm.query("users").where("id", 42).delete(); // 聚合 const count = await orm.query("users").count(); const avgAge = await orm.query("users").avg("age"); // 事务 await orm.transaction(async (tx) => { const [u] = await tx.unsafe("INSERT INTO users (name) VALUES ($1) RETURNING id", ["Bob"]); await tx.unsafe("INSERT INTO posts (title, user_id) VALUES ($1, $2)", ["Hi", u.id]); }); await orm.close(); ``` ### 装饰器风格 ```ts import { Orm, Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, OneToMany, ManyToOne, JoinColumn } from "bun-orm"; @Entity("users") class User { @PrimaryGeneratedColumn() id!: number; @Column({ length: 100 }) name!: string; @Column({ unique: true }) email!: string; @CreateDateColumn() createdAt!: string; @OneToMany(() => Post, (post) => post.user) posts!: Post[]; } @Entity("posts") class Post { @PrimaryGeneratedColumn() id!: number; @Column({ length: 200 }) title!: string; @ManyToOne(() => User, (user) => user.posts) @JoinColumn({ name: "userId" }) user!: User; @Column() userId!: number; } const orm = await Orm.connect({ filename: ":memory:" }); // 从装饰器实体同步表结构 await orm.em.sync([User, Post]); // 创建 const user = new User(); user.name = "Alice"; user.email = "alice@test.com"; await orm.em.save(user); // 查询 const users = await orm.em.find(User, { where: { name: "Alice" } }); const found = await orm.em.findById(User, 1); const count = await orm.em.count(User); // 懒加载关联 const posts = await found!.posts; // Promise // 更新 found!.name = "Alice Smith"; await orm.em.save(found!); // 删除 await orm.em.remove(found!); await orm.close(); ``` --- ## API 参考 ### 连接 三种数据库连接方式: ```ts // SQLite(内存) const orm = await Orm.connect({ filename: ":memory:" }); // SQLite(文件) const orm = await Orm.connect({ filename: "./data.db" }); // PostgreSQL const orm = await Orm.connect({ url: "postgres://user:pass@localhost:5432/mydb", }); // MySQL const orm = await Orm.connect({ hostname: "localhost", port: 3306, username: "root", password: "secret", database: "mydb", adapter: "mysql", }); // 连接池配置 const orm = await Orm.connect({ url: "postgres://localhost:5432/mydb", max: 20, // 最大连接数(默认 10) idleTimeout: 30, connectionTimeout: 10, }); ``` ### QueryBuilder — Builder 风格 #### SELECT ```ts // 全表查询 await orm.query("users"); // 指定列 await orm.query("users").select("id", "name", "email"); // 列别名 await orm.query("users").select("users.*", "users.name as full_name"); ``` #### WHERE 条件 ```ts // 简单等于 .where("name", "Alice") // 比较运算符 .where("age", ">", 18) .where("age", ">=", 18) .where("name", "!=", "Bob") .where("name", "LIKE", "%Ali%") // 对象形式(多个 AND) .where({ name: "Alice", age: 30 }) // IS NULL / IS NOT NULL .whereNull("deleted_at") .whereNotNull("email") // IN / NOT IN .whereIn("id", [1, 2, 3]) .whereNotIn("id", [4, 5]) // BETWEEN .whereBetween("age", [18, 65]) // 嵌套分组 .where(qb => qb.where("age", ">", 18).orWhere("role", "admin")) // 多条件链式 .where("status", "active") .andWhere("verified", true) .orWhere("role", "moderator") ``` #### JOIN ```ts // INNER JOIN .join("users", "users.id", "=", "posts.user_id") // LEFT JOIN .leftJoin("profiles", "profiles.user_id", "=", "users.id") // RIGHT JOIN .rightJoin("comments", "comments.post_id", "=", "posts.id") ``` #### 排序与分页 ```ts .orderBy("name", "asc") .orderBy("id", "desc") .limit(10) .offset(20) ``` #### GROUP BY / HAVING ```ts .groupBy("role") .having("count", ">", 5) ``` #### 聚合 ```ts await orm.query("users").count(); // SELECT COUNT(*) await orm.query("users").count("id"); // SELECT COUNT(id) await orm.query("users").sum("age"); // SELECT SUM(age) await orm.query("users").avg("age"); // SELECT AVG(age) await orm.query("users").min("age"); // SELECT MIN(age) await orm.query("users").max("age"); // SELECT MAX(age) ``` 聚合支持 WHERE 条件: ```ts await orm.query("users").where("role", "admin").count(); ``` #### INSERT ```ts // 插入 await orm.query("users").insert({ name: "Alice", email: "alice@test.com", }).execute(); // 插入并返回指定列(PostgreSQL / SQLite 支持) const [row] = await orm.query("users") .insert({ name: "Alice", email: "alice@test.com" }) .returning("id", "name", "email"); // row = { id: 1, name: "Alice", email: "alice@test.com" } ``` #### UPDATE ```ts await orm.query("users") .where("id", 1) .update({ name: "Updated" }) .execute(); // 带 RETURNING await orm.query("users") .where("id", 1) .update({ name: "Updated" }) .returning("id", "name"); ``` #### DELETE ```ts await orm.query("users").where("id", 1).delete().execute(); ``` #### Thenable `QueryBuilder` 实现 `PromiseLike`,可以直接 await: ```ts const rows = await orm.query("users").where("age", ">", 18); // 等价于 const rows = await orm.query("users").where("age", ">", 18).execute(); ``` #### 调试:查看生成的 SQL ```ts const { sql, params } = orm.query("users") .where("age", ">", 18) .orderBy("name", "asc") .limit(10) .toSQL(); console.log(sql); // SELECT * FROM "users" WHERE "age" > $1 ORDER BY "name" ASC LIMIT 10 console.log(params); // [18] ``` ### SchemaBuilder — DDL 操作 #### 建表 ```ts await orm.schema.createTable("products", (table) => { // 自增主键 table.increments("id"); // INTEGER PRIMARY KEY AUTOINCREMENT table.bigIncrements("id"); // BIGINT PRIMARY KEY AUTOINCREMENT // 基本类型 table.string("name", 100); // VARCHAR(100) table.text("description"); // TEXT table.integer("stock"); // INTEGER table.bigInteger("views"); // BIGINT table.boolean("active"); // BOOLEAN (方言适配) table.float("price"); // FLOAT / REAL table.datetime("published_at"); // DATETIME / TIMESTAMP table.json("metadata"); // JSON / JSONB (PG) table.uuid("uid"); // UUID (PG) table.blob("data"); // BLOB / BYTEA (PG) // 修饰符(施加于前一个列) table.string("email").unique().notNullable(); table.integer("age").default(0); table.integer("user_id").references("users.id"); // 时间戳快捷方法 table.timestamps(); // 创建 created_at 和 updated_at }); ``` #### 其他操作 ```ts // 删表 await orm.schema.dropTable("temp"); // 修改表 await orm.schema.alterTable("users", (t) => { t.addColumn("bio", "text"); t.dropColumn("old_col"); t.renameColumn("name", "full_name"); }); // 检查表是否存在 await orm.schema.hasTable("users"); // boolean // 重命名表 await orm.schema.renameTable("old", "new"); // 清空表 await orm.schema.truncate("logs"); ``` ### 装饰器参考 ```ts import { Entity, PrimaryGeneratedColumn, PrimaryColumn, Column, CreateDateColumn, UpdateDateColumn, OneToOne, OneToMany, ManyToOne, ManyToMany, JoinColumn, JoinTable, } from "bun-orm/decorator"; ``` | 装饰器 | 说明 | |--------|------| | `@Entity("table_name")` | 标记实体并指定表名(可选,默认类名小写) | | `@PrimaryGeneratedColumn()` | 自增主键 | | `@PrimaryColumn()` | 手动赋值主键 | | `@Column(opts)` | 普通列,支持 `type` `length` `nullable` `default` `unique` `name` | | `@CreateDateColumn()` | 插入时自动填入时间戳 | | `@UpdateDateColumn()` | 更新时自动填入时间戳 | | `@OneToOne(() => Target)` | 一对一关联 | | `@OneToMany(() => Target, x => x.owner)` | 一对多关联 | | `@ManyToOne(() => Target, x => x.items)` | 多对一关联 | | `@ManyToMany(() => Target)` | 多对多关联 | | `@JoinColumn({ name: "fk_col" })` | 指定外键列名 | | `@JoinTable({ name: "join_table" })` | 指定中间表名 | ### EntityManager — 装饰器风格 ```ts const em = orm.em; // 从装饰器实体同步表结构(创建不存在的表) await em.sync([User, Post]); // 保存(自动判断插入或更新) await em.save(entity); await em.saveMany([e1, e2]); // 查询 await em.find(User); // 全量 await em.find(User, { where: { name: "Alice" } }); // 条件 await em.find(User, { order: { name: "ASC" } }); // 排序 await em.find(User, { skip: 10, take: 20 }); // 分页 await em.findOne(User, { where: { email: "a@b.com" } }); await em.findById(User, 1); await em.count(User); await em.count(User, { where: { role: "admin" } }); // 删除 await em.remove(entity); await em.removeMany([e1, e2]); ``` ### 关联加载 ```ts // 懒加载 — 访问属性时自动查询 const user = await orm.em.findById(User, 1); const posts = await user.posts; // 首次访问触发查询 // 贪婪加载 — 通过 find options const users = await orm.em.find(User, { relations: ["posts"], }); ``` ### 迁移系统 ```ts import { Migration, MigrationRunner } from "bun-orm/migration"; // 定义迁移 class CreateUsersTable extends Migration { override name = "001_create_users"; override async up(conn: Connection) { await conn.unsafe(` CREATE TABLE users ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, email TEXT UNIQUE ) `); } override async down(conn: Connection) { await conn.unsafe("DROP TABLE IF EXISTS users"); } } // 运行迁移 const runner = orm.migrations; await runner.ensureTable(); // 查看待执行的迁移 const pending = await runner.pending([new CreateUsersTable()]); // 执行迁移 await runner.up([new CreateUsersTable()]); // 回滚 await runner.down([new CreateUsersTable()], 1); // 回滚最近 1 个 ``` --- ## 数据库方言差异 | 特性 | PostgreSQL | MySQL | SQLite | |------|-----------|-------|--------| | 标识符引用 | `"name"` | `` `name` `` | `"name"` | | 自增主键 | `SERIAL PRIMARY KEY` | `INT AUTO_INCREMENT PK` | `INTEGER PK AUTOINCREMENT` | | RETURNING | 支持 | 不支持 | 支持 | | NOW() | `NOW()` | `NOW()` | `datetime('now')` | ## 运行测试 ```bash bun test src/__tests__/ ``` ## License MIT