仓储 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)) // 未找到抛 OrmExceptionfindAll — 全表查询
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 / @AfterRemoveCond 条件查询
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) | 移除多对多关联 |