我们正在 JaveEE6 中开发一个项目,使用 EJB3 bean 和 JPA2 注释。
我们有一些有状态的 EJB3 bean,它们使用扩展的持久性上下文,以便将数据库实体显示在前面(通过一些接口(interface),消除对 DTO 的需要)。
典型的用法是这样的:
- 所有方法都没有事务,因为我们不想立即提交用户修改
- 通过非事务性方法,我们正在加载附加到扩展上下文的实体
- 只有一个保存方法是事务性的:在检查用户数据后,实体将被提交并保存在数据库中。
使用 MySQL 数据库,一切正常。
唉,在 Postgres 上,在非事务方法中加载的 @Lob
字段出现问题。 JDBC 似乎禁止 Lob 访问外部事务,抛出:
org.hibernate.exception.GenericJDBCException:大对象不能用于自动提交模式
作为 stackoverflower pointed ,一个Lob可以在多个记录中,所以需要一个事务来保持一致性。
在 persistence.xml
中将 autocommit
设置为 true 根本不起作用,而且 should not be done .
我不能使该方法具有事务性,因为我们不想在调用结束时提交任何内容。那么,有人知道我怎样才能简单地访问 Lob 吗?
我们设想的黑客解决方案是将 Lob 移动到另一个实体中,然后添加一个读取 Lob 内容的事务方法,以便我们可以使用它。我觉得很脏...
最佳答案
您的印象似乎是,对 JPA 上下文中加载的实体所做的更改会自动提交,除非实体已分离。 事实并非如此 显然它是如何工作的。但是,即使您修改附加实体并刷新,或者合并分离的实体,rollback
也会确保更改1 对其他事务永远不可见。
在执行只读操作时打开事务是无害的 - 而且通常是保持一致性的好主意,只要你不让它打开太久 2。如果你想保证没有数据被写入并且你正在使用 JTA,只需在 SessionContext
上使用 setRollbackOnly()
来确保这一点。对于手动 JPA 事务管理,只需确保在完成后调用 EntityTransaction
上的 rollback()
,而不是提交。
我个人建议在您的“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(...)
加载新的 EntityManager 副本LOB包含实体等
1。好的,所以在大多数数据库中都有一些事务豁免对象类型,例如 PostgreSQL SEQUENCE
和相关的 SERIAL
伪类型、咨询锁等甚至会受到影响通过回滚的事务。事务还可以在持有资源锁的意义上“写入”数据库,这也可能会阻止其他操作。对于实际的数据,它是安全的。
2。如果可以的话,请避免让 tx 打开超过几秒钟,因为长时间运行的事务会导致某些数据库出现性能问题,并且它们会占用连接池。避免在“用户思考时间”(您等待用户做某事的时间)内保持交易开放,他们可能会去做白日梦、去吃午饭、去度假或去月球……离开你的穷人等待返回的数据库和连接池。
关于postgresql - 如何从 EJB3 扩展持久性上下文访问 @Lob 字段,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11582149/