Nim 语言的高性能 ORM 框架 — 灵感来自 Python Peewee
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
let db = newSqliteDatabase(":memory:") # 内存数据库
let db = newSqliteDatabase("/path/to/db.sqlite") # 文件数据库
let db = newPostgresqlDatabase(
"mydb", "user", "password", "localhost", 5432
)
let db = newMySQLDatabase(
"mydb", "root", "secret", "localhost", 3306
)
let db = fromUrl("sqlite://:memory:")
let db = fromUrl("postgres://user:pass@localhost:5432/mydb")
let db = fromUrl("mysql://root:secret@localhost:3306/appdb")
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")
let tables = db.getTables() # 获取所有表名
let cols = db.getColumns("user") # 获取列信息
let idxs = db.getIndexes("user") # 获取索引
let fks = db.getForeignKeys("user") # 获取外键
Nimwee 提供 18+ 种字段类型,覆盖常见数据库列类型。所有字段通过统一的 applyOpts 机制处理选项,消除重复代码。
| 字段类型 | SQL 类型 | 说明 |
|---|---|---|
AutoField | INTEGER PRIMARY KEY | 自增主键 |
IntegerField | INTEGER | 32 位整数 |
BigIntegerField | BIGINT | 64 位整数 |
SmallIntegerField | SMALLINT | 16 位整数 |
FloatField | REAL | 单精度浮点 |
DoubleField | DOUBLE PRECISION | 双精度浮点 |
DecimalField | NUMERIC(p,s) | 定点数 |
CharField | VARCHAR(n) | 变长字符串 |
FixedCharField | CHAR(n) | 定长字符串 |
TextField | TEXT | 长文本 |
BlobField | BLOB | 二进制数据 |
BooleanField | INTEGER | 布尔值 |
DateTimeField | DATETIME | 日期时间 |
DateField | DATE | 日期 |
TimeField | TIME | 时间 |
TimestampField | INTEGER | 时间戳 |
UUIDField | TEXT | UUID 标识 |
JSONField | TEXT | JSON 数据 |
ForeignKeyField | INTEGER REFERENCES | 外键关联 |
所有字段接受统一的键值对选项:
# 必填 + 唯一
newCharField(100, ("null", "false"), ("unique", "true"))
# 默认值
newIntegerField(("default", "0"))
# 索引
newDateTimeField(("index", "true"))
# 外键 + 级联删除
newForeignKeyField("User", ("onDelete", "CASCADE"), ("backref", "tweets"))
所有字段工厂函数通过 defineFieldFactory 模板自动生成,选项处理统一委托给 applyOpts,消除了原版中每个字段类型重复的 case 语句。
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()
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 四种操作。
# 基本查询
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
let q = querySelect[ModelBase](db, "user")
.join("tweet", "user.id = tweet.user_id")
# JOIN 类型: jkInner(默认), jkLeftOuter, jkRightOuter, jkFullOuter, jkCross
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()
var iq = insertInto[ModelBase](db, "user")
iq["username"] = "charlie"
iq["bio"] = "chain insert"
let id = iq.execute()
let n = updateTable[ModelBase](db, "user")
.set("bio", "updated")
.where("username = ?", "charlie")
.execute()
let n = deleteFrom[ModelBase](db, "user")
.where("username = ?", "charlie")
.execute()
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'"))
let likeExpr = col("", "name").like(val("'%pattern%'"))
let containsExpr = col("", "name").contains(val("'pattern'"))
let startsExpr = col("", "name").startsWith(val("'pre'"))
let nullCheck = col("", "name").isNull()
let countExpr = fn("COUNT", @[col("", "id")])
let maxExpr = fn("MAX", @[col("", "price")])
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)
let castExpr = Expr(kind: ekCast, castExpr: col("", "price"), castType: "INTEGER")
# CAST(price AS INTEGER)
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()
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 | 初始化前 / 初始化后 |
let json = modelToJson(user)
# {"id": "1", "username": "alice", "bio": "Hello"}
var ds = newDataSet(db, "settings")
ds["theme"] = "dark"
ds["language"] = "zh"
echo ds["theme"] # "dark"
ds.del("theme")
| 类型 | 说明 |
|---|---|
DatabaseKind | 数据库类型枚举: dkSqlite, dkPostgres, dkMySQL |
Database | 数据库连接基类 |
ModelBase | 模型基类,包含数据和元信息 |
FieldMeta | 字段元数据容器 |
Expr | SQL 表达式 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](...) | 按索引字段查找 |
DatabaseKind、JoinKind、Expr AST、ColumnMeta 等基础类型与表达式操作符重载。
Database 连接管理、SQL 执行、事务、Schema 内省、URL 解析。
18+ 字段类型、统一 applyOpts 选项处理、SQL 类型映射、列定义生成。
ModelBase CRUD、表创建/删除、批量插入、字段访问、模型注册。
SelectQuery/InsertQuery/UpdateQuery/DeleteQuery 链式 API、SQL 生成、执行。
信号系统、迁移工具、JSON 序列化、DataSet、查询计数器。
62 项测试全部通过,覆盖:数据库操作、表达式系统(所有运算符/函数/CASE/子查询)、字段类型、模型 CRUD、查询构建器(SELECT/INSERT/UPDATE/DELETE 链式)、事务(提交/回滚/嵌套 Savepoint)、迁移、信号、序列化、URL 解析。