我们正在 JaveEE6 中开发一个项目,使用 EJB3 bean 和 JPA2 注释。

我们有一些有状态的 EJB3 bean,它们使用 扩展持久性上下文 以便将数据库实体显示在前面(通过一些接口(interface),消除对 DTO 的需要)。

典型的用途是这样的:

  • 所有方法都没有事务,因为我们不想立即提交用户修改
  • 通过非事务性方法,我们正在加载实体,附加到扩展上下文
  • 只有一个 save 方法是事务性的:在检查用户数据后,实体被提交并持久化到数据库中。


  • 使用 MySQL 数据库,一切正常。

    唉,在 Postgres 上,在非事务方法中加载的 @Lob 字段会出现问题。 JDBC 似乎禁止 Lob 访问外部事务,抛出:
    org.hibernate.exception.GenericJDBCException: Large Objects may not be used in auto-commit mode

    作为 stackoverflower pointed ,一个 Lob 可以在多条记录中,所以需要一个事务来保持一致性。

    autocommit 中将 persistence.xml 设置为 true 根本不起作用,还有 should not be done

    我不能使方法具有事务性,因为我们不想在调用结束时提交任何内容。那么,有谁知道我怎样才能简单地访问 Lob?

    我们想象的一个 hack 解决方案是将 Lob 移动到另一个实体中,然后添加一个读取 Lob 内容的事务方法,以便我们可以使用它。我觉得挺脏的...

    最佳答案

    您似乎认为对在 JPA 上下文中加载的实体所做的更改会自动提交,除非实体被分离。事实并非如此,它显然是如何工作的。但是,即使您修改附加实体并刷新,或者合并分离的实体,rollback 也可确保其他事务永远不会看到更改。

    在执行只读操作时打开事务是无害的——而且通常是保持一致性的好主意,只要你不要让它保持打开太长时间2。如果你想保证没有数据被写入并且你正在使用 JTA,只需在 setRollbackOnly() 上使用 SessionContext 来确保它。对于手动 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. 好的,所以在大多数数据库中都有一些事务豁免对象类型,例如 PostgreSQL SEQUENCE s 和相关的 SERIAL 伪类型、咨询锁等,甚至会受到回滚事务的影响。事务也可以“写入”数据库,因为它持有资源锁,这也可能阻止其他操作。对于实际数据,它是安全的。

    2. 如果可以的话,尽量避免让 tx 保持打开状态超过几秒钟,因为长时间运行的事务会导致某些数据库出现性能问题,并且会占用连接池。避免在“用户思考时间”(您等待用户做某事的时间)保持交易开放,他们可能会去做白日梦,或者去吃午饭,或者去度假,或者去月球……留下你的穷人数据库和连接池等待它们的返回。

    关于postgresql - 如何从 EJB3 扩展持久性上下文访问 @Lob 字段,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/11582149/

    10-13 03:26