You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Apache Cayenne同一事务中查询修改或新建的对象?

Cayenne同一事务内查询已修改/新建对象的最佳实践

和其他ORM框架类似,Cayenne的查询会直接读取数据库,但不同的是Cayenne仅在提交操作时才会将对象更改写入数据库。这就导致一个问题:同一事务内的查询无法看到已经做出但未提交的更改,示例如下:

ObjectContext ctx = runtime.newContext();
// 查询ID为1的Item
Item item = Cayenne.objectForPK(ctx, Item.class, 1);
System.out.println("" + item.getValue()); // 输出:42
// 修改值但不提交
item.setValue(84);

// 用新值查询数据库,找不到对象
Item copy1 = ObjectSelect.query(Item.class)
  .where(Item.VALUE.eq(84)).selectOne(ctx);
if (copy1 == null) System.out.println("not found"); // 输出:not found

// 用旧值查询数据库,可以找到对象
Item copy2 = ObjectSelect.query(Item.class)
  .where(Item.VALUE.eq(42)).selectOne(ctx);
// 但ObjectContext中缓存了对象的新值
System.out.println("" + copy2.getValue()); // 输出:84

有一种可行但比较繁琐的解决方案:同时查询数据库和ObjectContext的未提交对象:

Item copy = ObjectSelect.query(Item.class)
  .where(Item.VALUE.eq(84)).selectOne(ctx);
if (copy == null) copy = (Item) Item.VALUE.eq(84)
  .first(new ArrayList<>(ctx.uncommittedObjects()));
System.out.println("" + copy.getValue()); // 输出:"84"

那么,在同一事务中查找可能已修改或新建的对象,最佳实践是什么?


编辑后的解决方案

根据提示,正确的解决方案如下:

try {
  runtime.performInTransaction(() -> {
    ObjectContext ctx = runtime.newContext();
    Item item = Cayenne.objectForPK(ctx, Item.class, 31);
    item.setValue(84);
    ctx.commitChanges();
    Item copy = ObjectSelect.query(Item.class)
      .where(Item.VALUE.eq(84)).selectOne(ctx);
    System.out.println("" + copy.getValue()); // 输出:84
    ctx.rollbackChanges();
    return 0;
  });
} catch (Exception e) {
  // 回滚会触发CayenneRuntimeException
  System.out.println(e.toString()); 
}

如果回滚引发的异常不符合预期,可以使用以下辅助函数:

public <T> T performInTransaction(TransactionalOperation<T> op) {
  Transaction t = runtime.getInjector()
    .getInstance(TransactionFactory.class)
    .createTransaction();
  BaseTransaction.bindThreadTransaction(t);

  try {
    TransactionDescriptor td = TransactionDescriptor.builder()
      .propagation(TransactionPropagation.MANDATORY).build();
    T result = runtime.performInTransaction(op, td);
    BaseTransaction.bindThreadTransaction(null);

    if (t.isRollbackOnly()) t.rollback(); else t.commit();
    return result;
  } catch (Exception e) {
    t.rollback();
    throw e;
  }
}

内容的提问来源于stack exchange,提问作者PSt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 13:12:28