JPA混淆(托管与非托管实体)

时间:2011-09-15 16:18:30

标签: java-ee jpa transactions dao

我正在开展一个网络项目,试图了解一遍又一遍地做这种事情的最佳方式:

  • 从数据库
  • 中读取对象 A
  • 稍后,请阅读其他对象( B
  • 更改数据库中的 A B (作为交易的一部分 - 写全部更改或无)

旧的skool JDBC类型的方式不是问题,但JPA正在努力。

我需要在那里明确区分数据库更改发生的位置,并且根据我的观察,对管理实体的任何更改都将在下次调用EntityManager.commit()时进行修改(无论是否进行事务处理)是否在变更前明确开始)。正确的吗?

确保所有实体从不管理,并始终merge()是否更好?

我发现自己必须在这两个例子中做出决定:

RepaintAction1

User user = getUser(); //non-managed entity
Car car = getCar(123); //non-managed

car.setColor("Red");
user.setLog("Paints car red");

dao.update(car, user);

RepaintDAO1

entityTransaction.begin();
entityManager.merge(car);
entityManager.merge(user);
entityTransaction.commit();

或者:

RepaintAction2 (这与 RepaintAction1 相同,但与托管实体相同)

User user = getUser(); //managed entity
Car car = getCar(123); //managed

car.setColor("Red");
user.setLog("Paints car red");

dao.update(car, user);

RepaintDAO2

entityTransaction.begin();
entityManager.flush();
entityTransaction.commit();

第一个我不介意,但我必须忽略管理实体的一些优势(哪些?)。在第二个我不喜欢交易范围不明确的方式(以及如何处理回滚?)。

但这些是唯一的选择(例如,有没有办法使用托管实体明确划分交易)?处理这个问题的最佳方法是什么?

我为这么做而道歉,但是我经历了许多没有帮助的文档,我甚至不确定我所观察到的是正确的。

2 个答案:

答案 0 :(得分:11)

  1. 下次调用EntityManager.commit()时,将修改对托管实体的任何更改(无论是否在更改之前显式启动了事务)。 <强>正确吗 正确的。

  2. 确保所有实体永远不会受到管理,并始终merge() ,这样做会更好吗? 并非总是(或者他们不会让选择开放)但这很常见, 所以示例1是你最常找到的。

  3. 实际上并不需要示例2中的flush,提交将隐式刷新。

  4. 如何处理回滚?如果提交失败,持久性提供程序将回滚。如果应用程序容器收到系统异常,则会回滚现有事务。

  5. 托管实体的优势?延迟加载(没有LazyInitializationException)。此外,它会跟踪您的更改内容,因此您不会合并太少/几个实体。

  6. 有没有办法使用托管实体明确划分交易?我不清楚您有什么不清楚的地方。也许你的意思是不清楚是什么发生了变化,因为实体的变化发生在开始/提交边界之外。但是对于合并分离的实体也是如此,您可以更好地控制合并的内容,但是您没有确切地看到哪些属性发生了变化。

  7. 处理此问题的最佳方式是什么?通常,您的网络请求由交易服务(spring / ejb ...)处理。实体经理将由容器注入。通常,这些实体管理器是事务范围的(仅在事务发生时生效),因此在调用服务之前它们不存在。这意味着传递给它们的所有实体都不受管理。事务将在服务结束时提交(或回滚)。


  8. 注意:如果您考虑使用托管实体,这通常会与长期存在的EntityManagers一起使用。如果这样做,请注意EntityManagers不是线程安全的。

答案 1 :(得分:0)

关于第四个问题,可以通过以下方法进行回滚:

  1. 从标记为@Transactional
  2. 的方法中抛出异常
  3. 明确告诉事务它应该由TransactionAspectSupport.currentTransactionStatus()回滚.setRollbackOnly();