Skip to content

仓储 Repository ​

@Entity 宏在编译期自动生成 <Entity>Repository 类并注册为 Bean,直接 @Inject 使用。

注入 Repository ​

cangjie
package demo.controller

import ace_framework.*
import ace_orm.*
import ace_orm.macros.*
import demo.model.*

@Controller["/tasks"]
public class TaskController {
    @Inject
    var repo: TaskRepository

    @Get["/"]
    func list(): String {
        let tasks = repo.findAll()
        // 序列化 ...
    }
}

基础 CRUD ​

insert — 插入 ​

cangjie
let task = Task()
task.title    = "实现登录"
task.priority = 2
task.ownerId  = 1

let id = repo.insert(task)   // 返回自增 id(Int64)
// task.id 已被回填(insert 返回的 id 同步到实体)

save — 智能插入/更新 ​

cangjie
// id == 0 → INSERT;id != 0 → UPDATE
let id = repo.save(task)

TIP

@PrimaryColumn(非自增主键)以主键字段是否为空串来判断 insert/update。

findById — 按主键查询 ​

cangjie
match (repo.findById(DbInt(1))) {
    case Some(task) => println(task.title)
    case None       => println("not found")
}

findOneOrFail — 查不到抛异常 ​

cangjie
let task = repo.findOneOrFail(DbInt(1))   // 未找到抛 OrmException

findAll — 全表查询 ​

cangjie
let all = repo.findAll()   // 自动过滤软删除行(若有 @DeleteDateColumn)

update — 按主键更新 ​

cangjie
task.priority = 3
repo.update(task)

deleteById — 按主键删除(物理删除) ​

cangjie
repo.deleteById(DbInt(1))

remove — 删除实体(触发生命周期钩子) ​

cangjie
let task = repo.findById(DbInt(1)).getOrThrow()
repo.remove(task)   // 触发 @BeforeRemove / @AfterRemove

Cond 条件查询 ​

Cond 是类型安全的条件构建器,所有参数化,防 SQL 注入。

构建器速查 ​

cangjie
import ace_orm.*

eq("priority", DbInt(2))                    // col = ?
ne("status", DbText("draft"))              // col != ?
gt("score", DbReal(90.0))                  // col > ?
gte("age", DbInt(18))                      // col >= ?
lt("stock", DbInt(10))                     // col < ?
lte("price", DbReal(100.0))               // col <= ?
like("name", DbText("%Alice%"))            // col LIKE ?
ilike("email", DbText("%@gmail.com"))      // LOWER(col) LIKE LOWER(?)
between("age", DbInt(18), DbInt(60))       // col BETWEEN ? AND ?
isNull("deletedAt")                        // col IS NULL
isNotNull("publishedAt")                   // col IS NOT NULL
inList("status", [DbText("a"), DbText("b")]) // col IN (?,?)
notInList("role", [DbText("admin")])       // col NOT IN (?)
not(eq("status", DbText("banned")))       // NOT (col = ?)
orGroup([eq("role", DbText("admin")), eq("role", DbText("mod"))]) // (c1 OR c2)
andGroup([gte("age", DbInt(18)), isNotNull("email")])             // (c1 AND c2)
raw("created_at > NOW() - INTERVAL '7 days'", [])   // 原始片段

findBy — 多条件 AND 查询 ​

cangjie
let results = repo.findBy([
    eq("status", DbText("active")),
    gte("priority", DbInt(2))
])

findOneBy — 单条查询 ​

cangjie
match (repo.findOneBy([eq("email", DbText("alice@example.com"))])) {
    case Some(u) => u
    case None    => throw OrmException("user not found")
}

findOneByOrFail ​

cangjie
let user = repo.findOneByOrFail([eq("email", DbText("alice@example.com"))])

countBy — 按条件计数 ​

cangjie
let activeCount = repo.countBy([eq("status", DbText("active"))])

existsBy — 是否存在 ​

cangjie
if (repo.existsBy([eq("email", DbText("bob@example.com"))])) {
    throw OrmException("email already registered")
}

findAndCount — 查询 + 总数(分页用) ​

cangjie
let (items, total) = repo.findAndCount([
    eq("category", DbText("tech"))
])
// total 不受 limit/offset 影响(子查询计数)

findByComposite — 复合主键查询 ​

cangjie
match (repo.findByComposite([("userId", DbInt(1)), ("roleId", DbInt(5))])) {
    case Some(r) => ...
    case None    => ()
}

聚合 ​

cangjie
let total  = repo.count()                             // 总行数

// 带条件聚合
let avgScore = repo.avg("score", [eq("passed", DbBool(true))])
let maxAge   = repo.max("age", [])
let minPrice = repo.min("price", [eq("inStock", DbBool(true))])
let sumSales = repo.sum("amount", [eq("month", DbInt(7))])

// 返回 ?Float64,无行时返回 None
match (avgScore) {
    case Some(v) => println("avg: ${v}")
    case None    => println("no data")
}

软删除 ​

需要实体声明 @DeleteDateColumn[]。

cangjie
// 软删除(填充 deletedAt)
repo.softDelete([eq("id", DbInt(1))])

// 恢复(清除 deletedAt)
repo.restore([eq("id", DbInt(1))])

// 查询包含软删除的记录
let allIncludeDeleted = repo.withDeleted().findAll()
let deletedOnly = repo.withDeleted().findBy([isNotNull("deletedAt")])

repo.withDeleted() 返回一个新的 Repository 视图,原始 repo 不受影响。


批量操作 ​

insertMany — 批量插入(无事务,性能优先) ​

cangjie
let tasks = [t1, t2, t3]
repo.insertMany(tasks)

upsert — 插入或更新 ​

cangjie
let task = Task()
task.title = "Deploy"
task.slug  = "deploy-2026"

// 按 slug 列冲突时更新其他字段
repo.upsert(task, ["slug"])

方言实现:

  • SQLite / PG:INSERT ... ON CONFLICT (slug) DO UPDATE SET ...
  • MySQL:INSERT ... ON DUPLICATE KEY UPDATE ...

updateWhere — 按条件批量更新 ​

cangjie
repo.updateWhere(
    [eq("status", DbText("pending"))],
    [("status", DbText("expired")), ("updatedAt", DbInt(nowMs))]
)

deleteWhere — 按条件批量删除 ​

cangjie
repo.deleteWhere([lt("createdAt", DbInt(cutoffMs))])

increment / decrement — 原子计数 ​

cangjie
repo.increment([eq("id", DbInt(articleId))], "viewCount", 1)
repo.decrement([eq("id", DbInt(userId))], "credits", 10)

clear — 清空表 ​

cangjie
repo.clear()   // DELETE FROM table(危险!生产谨慎使用)

流式读取(大数据集) ​

getMany() 一次全量加载,百万行会 OOM。使用 stream 分批回调:

cangjie
// 每 1000 行回调一次,适合导出/转换
repo.stream(1000, {batch =>
    for (task in batch) {
        exportToCsv(task)
    }
})
  • SQLite / MySQL:内部用 LIMIT/OFFSET 分批
  • PostgreSQL:使用原生 DECLARE/FETCH/CLOSE 游标(高效,不回传全量)

QueryBuilder 也支持 stream

cangjie
repo.createQueryBuilder("t")
    .andWhere("t.status", "=", DbText("active"))
    .orderBy("t.createdAt", "ASC")
    .stream(500, {batch => process(batch)})

QueryBuilder 入口 ​

cangjie
// SELECT 查询构造器
let qb = repo.createQueryBuilder("t")

// INSERT 构造器
let ib = repo.createInsertQueryBuilder()

// UPDATE 构造器
let ub = repo.createUpdateQueryBuilder()

// DELETE 构造器
let db = repo.createDeleteQueryBuilder()

详见 QueryBuilder 文档。


API 速查表 ​

查询 ​

方法说明
findById(id)按主键查,返回 ?T
findOneOrFail(id)查不到抛 OrmException
findAll()全表,自动软删除过滤
findBy(conds)多条件 AND
findOneBy(conds)取第一条
findOneByOrFail(conds)取不到抛异常
findAndCount(conds)返回 (Array<T>, Int64)
findByComposite(pairs)复合主键
findOne(id, relations)按主键 + LEFT JOIN 关联
loadRelations(e, rels)懒加载对象关联
loadCollection(e, field)懒加载集合

写入 ​

方法说明
insert(e)插入,返回 Int64 id
save(e)insert/update 二合一
insertMany(es)批量插入
update(e)按主键更新
upsert(e, cols)ON CONFLICT UPDATE
updateWhere(conds, sets)按条件批量更新
increment(conds, col, by)原子自增
decrement(conds, col, by)原子自减

删除 ​

方法说明
deleteById(id)物理删除
remove(e)删除 + 触发钩子
deleteWhere(conds)按条件批量删除
softDelete(conds)软删除(填充 deletedAt)
restore(conds)恢复软删除
clear()清空整表

聚合 ​

方法返回说明
count()Int64总行数
countBy(conds)Int64条件计数
existsBy(conds)Bool是否存在
sum(col, conds)?Float64求和
avg(col, conds)?Float64平均值
min(col, conds)?Float64最小值
max(col, conds)?Float64最大值

其他 ​

方法说明
stream(chunkSize, cb)流式批量读
withDeleted()返回含软删除的视图
noCascade()禁用级联(关联操作)
countRelation(e, field)统计关联集合数
addRelation(e, field, id)添加多对多关联
removeRelation(e, field, id)移除多对多关联

基于 Apache-2.0 许可证发布