Nimwee

Nim 语言的高性能 ORM 框架 — 灵感来自 Python Peewee

SQLite PostgreSQL MySQL 链式查询 62 项测试全通过
3
数据库后端
18+
字段类型
62
测试用例
6
核心模块

安装与配置

依赖安装

Nimwee 依赖 db_connector 包提供数据库连接支持:

# 安装 db_connector
nimble install db_connector

# 克隆 Nimwee
git clone https://github.com/yourorg/nimwee
cd nimwee

项目集成

在你的 .nimble 文件中添加依赖,或直接通过路径导入:

import nimwee  # 如果已安装

# 或通过路径导入
import /path/to/nimwee/src/nimwee

运行测试

nim c -r --path:src tests/test_nimwee.nim

快速开始

以下示例展示 Nimwee 的核心用法 — 从连接数据库到完成增删改查:

import nimwee

# 1. 连接数据库
let db = newSqliteDatabase(":memory:")

# 2. 定义字段
var fields = @[
  FieldMeta(fieldObj: newAutoField(), fieldType: ftAuto),
  FieldMeta(fieldObj: newCharField(100, ("null", "false")), fieldType: ftChar),
  FieldMeta(fieldObj: newTextField(), fieldType: ftText),
]
fields[0].fieldObj.name = "id"
fields[1].fieldObj.name = "username"
fields[2].fieldObj.name = "bio"

# 3. 注册模型并建表
let model = defModel("User", fields, db)
model.createTable()

# 4. 插入记录
var user = newModel(db, "user", fields)
user["username"] = "alice"
user["bio"] = "Hello World"
discard user.insert()

# 5. 查询记录
var u = newModel(db, "user", fields)
discard u.getById(1)
echo u["username"]  # "alice"

# 6. 链式查询
let cnt = querySelect[ModelBase](db, "user")
  .where("username = ?", "alice")
  .count()
echo cnt  # 1

数据库连接

SQLite

let db = newSqliteDatabase(":memory:")  # 内存数据库
let db = newSqliteDatabase("/path/to/db.sqlite")  # 文件数据库

PostgreSQL

let db = newPostgresqlDatabase(
  "mydb", "user", "password", "localhost", 5432
)

MySQL

let db = newMySQLDatabase(
  "mydb", "root", "secret", "localhost", 3306
)

URL 方式连接

let db = fromUrl("sqlite://:memory:")
let db = fromUrl("postgres://user:pass@localhost:5432/mydb")
let db = fromUrl("mysql://root:secret@localhost:3306/appdb")

原始 SQL 执行

db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT)")
db.exec("INSERT INTO test (name) VALUES (?)", "hello")

let val = db.getValue("SELECT name FROM test WHERE id = ?", "1")
let row = db.getRow("SELECT * FROM test WHERE id = ?", "1")
let rows = db.getAllRows("SELECT * FROM test")

Schema 内省

let tables = db.getTables()       # 获取所有表名
let cols = db.getColumns("user")  # 获取列信息
let idxs = db.getIndexes("user")  # 获取索引
let fks = db.getForeignKeys("user")  # 获取外键

字段类型系统

Nimwee 提供 18+ 种字段类型,覆盖常见数据库列类型。所有字段通过统一的 applyOpts 机制处理选项,消除重复代码。

字段类型SQL 类型说明
AutoFieldINTEGER PRIMARY KEY自增主键
IntegerFieldINTEGER32 位整数
BigIntegerFieldBIGINT64 位整数
SmallIntegerFieldSMALLINT16 位整数
FloatFieldREAL单精度浮点
DoubleFieldDOUBLE PRECISION双精度浮点
DecimalFieldNUMERIC(p,s)定点数
CharFieldVARCHAR(n)变长字符串
FixedCharFieldCHAR(n)定长字符串
TextFieldTEXT长文本
BlobFieldBLOB二进制数据
BooleanFieldINTEGER布尔值
DateTimeFieldDATETIME日期时间
DateFieldDATE日期
TimeFieldTIME时间
TimestampFieldINTEGER时间戳
UUIDFieldTEXTUUID 标识
JSONFieldTEXTJSON 数据
ForeignKeyFieldINTEGER REFERENCES外键关联

字段选项

所有字段接受统一的键值对选项:

# 必填 + 唯一
newCharField(100, ("null", "false"), ("unique", "true"))

# 默认值
newIntegerField(("default", "0"))

# 索引
newDateTimeField(("index", "true"))

# 外键 + 级联删除
newForeignKeyField("User", ("onDelete", "CASCADE"), ("backref", "tweets"))
精简优化

所有字段工厂函数通过 defineFieldFactory 模板自动生成,选项处理统一委托给 applyOpts,消除了原版中每个字段类型重复的 case 语句。

模型定义与 CRUD

定义模型

var fields = @[
  FieldMeta(fieldObj: newAutoField(), fieldType: ftAuto),
  FieldMeta(fieldObj: newCharField(100, ("unique", "true")), fieldType: ftChar),
  FieldMeta(fieldObj: newTextField(), fieldType: ftText),
]
fields[0].fieldObj.name = "id"
fields[1].fieldObj.name = "username"
fields[2].fieldObj.name = "bio"

let model = defModel("User", fields, db)
model.createTable()

便捷构造器 newModel

var user = newModel(db, "user", fields)
user["username"] = "alice"
user["bio"] = "Hello"

增删改查

插入

let id = user.insert()  # 返回自增 ID
echo id  # 1

查询

var u = newModel(db, "user", fields)
discard u.getById(1)
echo u["username"]  # "alice"

# 条件查询
discard u.get("username = 'alice'")

更新

u["bio"] = "Updated"
discard u.save()  # 自动判断 insert 或 update

删除

discard u.delete()

批量插入

let rows = @[
  {"username": "user1"}.newTable(),
  {"username": "user2"}.newTable(),
]
discard model.insertMany(rows)

查询构建器

Nimwee 提供流畅的链式查询 API,支持 SELECT、INSERT、UPDATE、DELETE 四种操作。

SELECT 查询

# 基本查询
let q = querySelect[ModelBase](db, "user")

# 链式调用
let q = querySelect[ModelBase](db, "user")
  .where("age > ?", "18")
  .orderBy("created_at DESC")
  .limit(10)
  .offset(20)

let sql = q.toSql()
# SELECT * FROM "user" WHERE age > ? ORDER BY created_at DESC LIMIT 10 OFFSET 20

分页

let q = querySelect[ModelBase](db, "user")
  .paginate(2, 20)  # 第 2 页,每页 20 条
# LIMIT 20 OFFSET 20

JOIN

let q = querySelect[ModelBase](db, "user")
  .join("tweet", "user.id = tweet.user_id")
  # JOIN 类型: jkInner(默认), jkLeftOuter, jkRightOuter, jkFullOuter, jkCross

DISTINCT / FOR UPDATE

let q = querySelect[ModelBase](db, "user", "username")
  .`distinct`()

let q2 = querySelect[ModelBase](db, "user")
  .forUpdate()

聚合查询

let cnt = querySelect[ModelBase](db, "user").count()
let exists = querySelect[ModelBase](db, "user")
  .where("username = ?", "alice").exists()

INSERT 链式

var iq = insertInto[ModelBase](db, "user")
iq["username"] = "charlie"
iq["bio"] = "chain insert"
let id = iq.execute()

UPDATE 链式

let n = updateTable[ModelBase](db, "user")
  .set("bio", "updated")
  .where("username = ?", "charlie")
  .execute()

DELETE 链式

let n = deleteFrom[ModelBase](db, "user")
  .where("username = ?", "charlie")
  .execute()

getOrCreate

let (inst, created) = getOrCreate[ModelBase](
  ModelBase, db, "user",
  {"bio": "default"},      # 默认值
  {"username": "bob"}    # 查找条件
)

表达式系统

Nimwee 提供类型安全的 SQL 表达式 AST,支持比较、逻辑、函数、CASE WHEN、子查询等。

比较运算

let cond1 = col("users", "age") > lit(18)
let cond2 = col("users", "name") == val("'alice'")
let cond3 = col("u", "id") != lit(1)

逻辑运算

let andCond = (col("u", "age") > lit(18)) & (col("u", "active") == lit(1))
let orCond = (col("u", "name") == val("'alice'")) | (col("u", "name") == val("'bob'"))
let notCond = ~(col("u", "status") == val("'deleted'"))

LIKE / 模式匹配

let likeExpr = col("", "name").like(val("'%pattern%'"))
let containsExpr = col("", "name").contains(val("'pattern'"))
let startsExpr = col("", "name").startsWith(val("'pre'"))

IS NULL

let nullCheck = col("", "name").isNull()

SQL 函数

let countExpr = fn("COUNT", @[col("", "id")])
let maxExpr = fn("MAX", @[col("", "price")])

CASE WHEN

let caseExpr = Expr(
  kind: ekCase,
  caseWhens: @[(col("", "status") == val("'A'"), lit(1))],
  caseElse: lit(0)
)
# CASE WHEN status = 'A' THEN 1 ELSE 0 END

子查询

let sub = Expr(kind: ekSubQuery, subSql: "SELECT id FROM users WHERE active = 1")
# (SELECT id FROM users WHERE active = 1)

CAST

let castExpr = Expr(kind: ekCast, castExpr: col("", "price"), castType: "INTEGER")
# CAST(price AS INTEGER)

原始 SQL

let rawExpr = raw("NOW()")
let rawExpr2 = SQL("CURRENT_TIMESTAMP")

事务管理

Nimwee 支持嵌套事务(通过 SAVEPOINT 实现),自动跟踪事务深度。

基本事务

db.begin()
db.exec("INSERT INTO user (username) VALUES (?)", "alice")
db.commit()  # 或 db.rollback()

嵌套事务(Savepoint)

db.begin()                           # 开启外层事务
db.exec("INSERT ...")               # 外层操作

  db.begin()                         # 创建 SAVEPOINT
  db.exec("INSERT ...")             # 内层操作
  db.rollback()                      # 回滚到 SAVEPOINT(内层操作撤销)

db.commit()                          # 提交外层事务

事务状态

echo db.inTx     # false — 是否在事务中
echo db.txDepth  # 0 — 当前事务嵌套深度

数据库迁移

let mig = newMigrator(db)

# 添加列
discard mig.addColumn("user", "email", "TEXT", "NOT NULL DEFAULT ''")

# 删除列
discard mig.dropColumn("user", "old_field")

# 重命名列
discard mig.renameColumn("user", "name", "full_name")

# 添加索引
discard mig.addIndex("user", @["email"], unique = true)

# 重命名表
discard mig.renameTable("old_users", "users")

# 执行迁移
mig.run()

信号系统

信号系统允许在模型操作前后执行自定义逻辑(钩子/回调)。

# 连接信号处理器
connect(skPreSave, proc(sender: string, instance: ModelBase, created: bool) =
  echo "即将保存: ", sender
, "my_handler")

# 触发信号
emitSignal(skPreSave, "User", myModel, true)

# 断开信号
disconnect(skPreSave, "my_handler")
信号类型触发时机
skPreSave / skPostSave保存前 / 保存后
skPreDelete / skPostDelete删除前 / 删除后
skPreInit / skPostInit初始化前 / 初始化后

序列化

模型转 JSON

let json = modelToJson(user)
# {"id": "1", "username": "alice", "bio": "Hello"}

DataSet 键值存储

var ds = newDataSet(db, "settings")
ds["theme"] = "dark"
ds["language"] = "zh"
echo ds["theme"]  # "dark"
ds.del("theme")

API 参考

核心类型

类型说明
DatabaseKind数据库类型枚举: dkSqlite, dkPostgres, dkMySQL
Database数据库连接基类
ModelBase模型基类,包含数据和元信息
FieldMeta字段元数据容器
ExprSQL 表达式 AST 节点
SelectQuery[T]SELECT 查询构建器
InsertQuery[T]INSERT 查询构建器
UpdateQuery[T]UPDATE 查询构建器
DeleteQuery[T]DELETE 查询构建器
Migrator数据库迁移工具
MetaOptions模型元信息(表名、数据库、索引等)

核心函数

函数说明
newSqliteDatabase(path)创建 SQLite 连接
newPostgresqlDatabase(...)创建 PostgreSQL 连接
newMySQLDatabase(...)创建 MySQL 连接
fromUrl(url)通过 URL 创建连接
defModel(name, fields, db)定义并注册模型
newModel(db, table, fields)创建模型实例
querySelect[T](db, table)创建 SELECT 查询
insertInto[T](db, table)创建 INSERT 查询
updateTable[T](db, table)创建 UPDATE 查询
deleteFrom[T](db, table)创建 DELETE 查询
col(table, name)创建列表达式
val(s) / lit(v)创建值/字面量表达式
raw(sql) / SQL(s)创建原始 SQL 表达式
fn(name, args)创建函数调用表达式
quoteId(name, dbKind)引号包裹标识符
getOrCreate[T](...)查找或创建记录
getByIndex[T](...)按索引字段查找

模块结构

core.nim

核心定义

DatabaseKind、JoinKind、Expr AST、ColumnMeta 等基础类型与表达式操作符重载。

database.nim

数据库层

Database 连接管理、SQL 执行、事务、Schema 内省、URL 解析。

fields.nim

字段系统

18+ 字段类型、统一 applyOpts 选项处理、SQL 类型映射、列定义生成。

model.nim

模型系统

ModelBase CRUD、表创建/删除、批量插入、字段访问、模型注册。

query.nim

查询构建器

SelectQuery/InsertQuery/UpdateQuery/DeleteQuery 链式 API、SQL 生成、执行。

utils.nim

工具模块

信号系统、迁移工具、JSON 序列化、DataSet、查询计数器。

测试覆盖

62 项测试全部通过,覆盖:数据库操作、表达式系统(所有运算符/函数/CASE/子查询)、字段类型、模型 CRUD、查询构建器(SELECT/INSERT/UPDATE/DELETE 链式)、事务(提交/回滚/嵌套 Savepoint)、迁移、信号、序列化、URL 解析。