您似乎认为对 JPA 上下文中加载的实体所做的更改会自动提交,除非该实体已分离。事实并非如此,它显然是如何工作的。但是,即使您修改附加的实体并刷新,或者合并分离的实体,a也rollback
可以确保更改1对其他事务永远不可见。
在执行只读操作时打开事务是无害的,而且通常是保持一致性的好主意,只要您不要将其打开太久2。如果你想保证没有数据被写入并且你正在使用 JTA,只需使用setRollbackOnly()
onSessionContext
来确保它。对于手动 JPA 事务管理,只需确保在完成时调用rollback()
,EntityTransaction
而不是提交。
就个人而言,我建议在您的“getLob”方法中使用新事务并在方法结束时将其回滚。如果您的数据库不支持嵌套事务(很少支持),这通常会导致从池中获取一个新连接来执行这项工作。
如果您使用 JTA 和容器管理事务,请尝试:
@Stateless
@TransactionManagement(TransactionManagementType.CONTAINER)
public class LobTest {
@PersistenceContext
private EntityManager em;
@Resource
private SessionContext sctx;
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public byte[] getLob() {
// Get your LOB content by fetching a new copy of the entity from the DB
// by ID, avoiding the need to split the LOB out. Note that you lose
// tx consistency guarantees between the LOB and the rest of the entity by
// doing this.
// then after loading the LOB:
sctx.setRollbackOnly();
}
}
或者,如果您不介意读取 LOB 中止任何周围事务的错误,请使用TransactionAttributeType.REQUIRES
代替REQUIRES_NEW
和不setRollbackOnly()
。你不能改变任何东西,所以什么都不会被承诺。如果一个新事务尚未打开,它将打开一个新事务,否则加入现有事务,因此您可以获得对 LOB 的一致读取。唯一的缺点是一些数据库错误会中止整个 JTA 事务。
如果您在非 JTA 环境中使用用户管理的事务,只需获取新的 EntityManager、获取 EntityTransaction、用于em.find(...)
加载包含实体的 LOB 的新副本等。
1 . 好的,所以在大多数数据库中都有一些事务豁免对象类型,例如 PostgreSQLSEQUENCE
和相关的SERIAL
伪类型、咨询锁等,即使受到回滚事务的影响。事务也可以“写入”到数据库,因为它持有资源锁,这也可能阻止其他操作。对于实际数据,它是安全的。
2 . 如果可以的话,请避免让 tx 保持打开状态超过几秒钟,因为长时间运行的事务会导致某些数据库出现性能问题,并且会占用连接池。避免在“用户思考时间”(您正在等待用户做某事的时间)保持交易开放,他们可能会做白日梦,或者去吃午饭,或者去度假,或者去月球......离开你的穷人数据库和连接池等待它们的返回。