Skip to content

仓储 Repository

为每个 @Entity 类,ACE ORM 在编译期自动生成一个对应的 <Entity>Repository 类并注册为 Bean。无需手动继承任何基类——直接用 @Inject 注入即可使用。

注入 Repository

cangjie
package demo.controller

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

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

    @Get["/"]
    func list(): Array<Task> {
        repo.findAll()
    }
}

基础 CRUD 方法

所有 Repository 均实现 Repository<T> 接口,提供以下方法:

方法签名说明
insert(t: T): T插入记录,返回带自增 id 的实体
findById(id: DbValue): Option<T>按主键查询,未找到返回 None
findAll(): Array<T>查询全部记录
findAll(where: String, params: Array<DbValue>): Array<T>带条件查询
update(t: T): Bool按主键更新,返回是否成功
delete(id: DbValue): Bool按主键删除,返回是否成功
count(): Int64返回总记录数
query(sql: String, params: Array<DbValue>): Array<T>执行自定义 SQL

插入与查询

cangjie
// 插入
let task = Task()
task.title    = "实现登录接口"
task.priority = 2
task.owner    = "alice"

let saved = repo.insert(task)
// saved.id 已被填充为数据库自增 id

// 按主键查询
match (repo.findById(DbInt(saved.id))) {
    case Some(t) => println(t.title)
    case None    => println("未找到")
}

// 查询全部
let all = repo.findAll()

条件查询

findAll 支持传入 WHERE 子句和绑定参数,防止 SQL 注入:

cangjie
// 查询 priority > 1 的任务
let urgent = repo.findAll(
    where: "priority > ?",
    params: [DbInt(1)]
)

// 多条件组合
let mine = repo.findAll(
    where: "owner = ? AND priority >= ?",
    params: [DbString("alice"), DbInt(2)]
)

DbValue 类型速查

仓颉类型包装为
Int64DbInt(v)
StringDbString(v)
Float64DbFloat(v)
BoolDbBool(v)
NoneDbNull

手动 SQL 查询

当内置方法无法满足需求时,使用 query 执行原生 SQL:

cangjie
// 聚合查询:按 owner 统计任务数
let rows = repo.query(
    "SELECT owner, COUNT(*) as cnt FROM tasks GROUP BY owner",
    params: []
)

// 复杂 JOIN(关联表需手动映射)
let highPriority = repo.query(
    "SELECT t.* FROM tasks t WHERE t.priority = (SELECT MAX(priority) FROM tasks)",
    params: []
)

query 返回值

query 返回 Array<T>,ACE ORM 按列名自动映射到实体字段。若 SELECT 列与字段名不一致,需使用 AS 别名对齐字段名。

更新与删除

cangjie
// 先查后改
match (repo.findById(DbInt(1))) {
    case Some(t) =>
        t.priority = 3
        repo.update(t)
    case None => ()
}

// 删除
let ok = repo.delete(DbInt(1))
if (!ok) {
    println("记录不存在或已删除")
}

统计

cangjie
let total = repo.count()
println("共 ${total} 条任务")

事务支持

需要跨多个操作保证原子性时,在 Service 方法上使用 @Transactional。详见 @Transactional 文档

cangjie
package demo.service

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

@Service
public class TaskService {
    @Inject
    var repo: TaskRepository

    @Transactional
    public func createAndLink(title: String, parentId: Int64): Task {
        let task = Task()
        task.title = title
        let saved = repo.insert(task)
        // 若此处抛出异常,insert 会自动回滚
        linkToParent(saved.id, parentId)
        saved
    }
}

并发注意

Repository Bean 默认为单例作用域,其内部持有数据库连接池而非单一连接,并发安全。但若直接操作底层连接对象,请确保在同一事务内使用同一连接。

禁止在 @Entity 类中持有可变共享状态

实体类是纯数据载体,不要在字段中缓存业务对象或注入其他 Bean——Repository 查询每次返回新的实体实例,跨请求共享实体对象会导致数据竞争。

基于 Apache-2.0 许可证发布