2012-07-20 34 views
3

我们正在开发一个项目,JaveEE6访问@Lob场,使用EJB3豆类和JPA2注解。如何从一个EJB3扩​​展持久化上下文

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

典型的使用是一样的东西:

  • 所有的方法都是没有交易,因为我们不希望立即通过非事务性方法提交用户修改
  • ,我们正在装载实体,附加到扩展上下文
  • 只有一种保存方法是事务性的:在检查用户数据之后,实体被提交并保存在数据库中。

与MySQL数据库,一切都运行得很好。

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


作为stackoverflower pointed,一个LOB可以在多个记录,所以它需要一个事务保持一致。

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

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

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

回答

0

由于某些体系结构原因,我们选择在另一个Entity中设置Lob字段,并通过@Stateless bean读取/写入该字段。

实体:

@Entity 
@Access(AccessType.FIELD) 
public class LobEntity 
{ 
    [...] 

    @Lob 
    private String content; 

    public String getContent() 
    { 
     return content; 
    } 

    public void setContent(String content) 
    { 
     this.content = content; 
    } 
} 

服务:

@Stateless 
@LocalBean 
public class LobService 
{ 
    @PersistenceContext 
    private EntityManager em; 

    public String readLob(Long lobId) 
    { 
     LobEntity lobEntity = em.find(LobEntity.class, lobId); 
     return lobEntity.getContent(); 
    } 

    public LobEntity writeNewLob(String content) 
    { 
     LobEntity lob = new LobEntity(content); 
     em.persist(lob); 
     return lob; 
    } 
} 

这直接包含高球一类,但没有更多:

@Entity 
@Access(value = AccessType.FIELD) 
public class MyEntity 
{ 
    [...] 

    protected Long contentLobId; 
    @Transient 
    protected String editableContent; 

    public Long getContentLobId() 
    { 
     return contentLobId; 
    } 

    public void setContentLobId(Long contentLobId) 
    { 
     this.contentLobId = contentLobId; 
    } 

    public String getEditableContent() 
    { 
     return editableContent; 
    } 

    public void setEditableContent(String editableContent) 
    { 
     this.editableContent = editableContent; 
    } 
} 

实体不具备LobEntity本身,以避免开发人员尝试像傻瓜一样访问它:如果我们想要内容在非事务性上下文中,我们使用的是LobService。当我们想保存编辑的内容时,我们也使用相同的bean。

0

尝试getEntityManager().flush();

此写入数据库,但不提交当前事务。假设你的隔离级别是“读取提交”,那么在你实际提交事务之前,你不会在其他查询中看到更新。请记住,您将在所触摸的行上持有锁...

2

您似乎觉得对JPA上下文中加载的实体的更改会自动提交,除非该实体已分离。这是而不是,实际上它显然是如何工作的。但是,即使您修改附加实体并刷新,或者合并了分离的实体,也可以确保其他事务永远不会看到更改。

它是无害的 - 而且往往一致性是一个好主意 - 执行只读操作,当这么久有交易开放只要你不把它开得太久 。如果你想保证没有数据被写入,并且你正在使用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和Don” t setRollbackOnly()。你不能改变任何东西,所以没有什么会被提交。如果尚未打开,它将打开一个新事务,否则将加入现有事务,以便您可以一致地读取LOB。唯一的缺点是一些数据库错误会中止整个JTA事务。

如果您使用用户管理的事务与非JTA环境中,仅仅获得一个新的EntityManager,得到一个EntityTransaction,使用em.find(...)加载包含实体LOB的新副本等


。好的,所以在大多数数据库中都有一些不需要事务处理的对象类型,比如PostgreSQL SEQUENCE和相关的SERIAL伪类型,咨询锁等等,即使回滚的事务也会受到这种类型的影响。事务也可以“锁定”数据库,从而锁定可能阻止其他操作的资源。对于实际的数据,这是安全的。

。如果可以的话,避免让tx保持打开状态的时间超过几秒钟,因为长时间运行的事务会导致某些数据库出现性能问题,并且会阻塞连接池。避免在“用户的思考时间”内保持交易 - 当你等待用户做某件事时 - 他们可能会做白日梦,午餐,度假或月球......让你的穷人数据库和连接池等待他们的回报。

+0

这是一个非常类似于我们想过的解决方案(添加一个读取Lob内容的事务方法,以便我们可以使用它)。 我认为如果没有奇迹发生,我们会为之努力,因为它是我们能找到的最好的。 – 2012-07-25 08:05:33

+0

@XavierPortebois可惜的是PgJDBC无法让你获得整个LOB的更透明,从而关注幕后的事务管理。也许你应该看看PgJDBC的来源?毕竟它是开源的,你可能会加强它以适应你的需求。 – 2012-07-25 13:53:43

+1

哇。在写这篇文章的时候,我发现JTA bean管理的事务没有相同的'REQUIRES_NEW'。他们不能暂停和恢复交易。如果您希望您使用*的功能来使用容器管理的事务。 – 2012-07-26 00:00:27

相关问题