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 许可证发布