首先,我有一个无状态 bean,它执行简单的检索,如下所示。
@Stateless
@LocalBean
public A {
@PersistenceContext
private EntityManager em;
public MyEntity retrieveMethod(){
em.createQuery(...).getSingleResult();
}
}
我有一个有状态 bean,用于管理与远程客户端的长时间对话,它看起来像这样:
@Statefull
@LocalBean
@TransactionAttribute(NOT_SUPPORTED)
public class B implements BRemote {
@PersistenceContext(type = EXTENDED)
private EntityManager em;
@EJB
A a;
public void start(){
OtherEntity oe = new OtherEntity();
oe.setRelationMyEntitie(this.a.retrieveMethod());
em.persist(oe);
}
@TransactionAttribute(REQUIRED)
public void end(){
em.flush();
}
}
执行 em.persist(oe) 时出现问题。 oe 具有对由另一个 EntityManager 加载的 MyEntity 实例的引用。所以他们不知道它在抱怨持久分离的实体。
我想知道有什么办法可以避免这个问题。如果没有直接的解决方案,最好采用什么模式?
编辑:我不想在 start() 上使用事务,因为在实际应用程序中,有状态 bean 用于实现需要立即持久化的复杂实体模型。我尝试设置此处描述的名为 session-per-conversation 的模式 http://docs.jboss.org/hibernate/core/4.0/manual/en-US/html/transactions.html#transactions-basics-apptx。因此,如果我理解正确,解决方案是“在 bean B 的 start() 方法中使用事务”,但如果我这样做,在方法结束时,内容将刷新到数据库,这不是我想要的。
我可以看到的其他解决方案是在 B 的 EntityManager 中获取 MyEntity,因此进行合并,或 em.find() 或将retrieveMethod委托给某些 DAO 样式类,使用 em 参数,并在 bean A 中执行一个简单的操作委托给 DAO,在 bean B 中直接调用 DAO。
知道什么是最好的方法吗?
First, I have a stateless bean which do a simple retreive, looking like this.
@Stateless
@LocalBean
public A {
@PersistenceContext
private EntityManager em;
public MyEntity retrieveMethod(){
em.createQuery(...).getSingleResult();
}
}
I have a statefull bean used to manage long conversation with a remote client, it look like this :
@Statefull
@LocalBean
@TransactionAttribute(NOT_SUPPORTED)
public class B implements BRemote {
@PersistenceContext(type = EXTENDED)
private EntityManager em;
@EJB
A a;
public void start(){
OtherEntity oe = new OtherEntity();
oe.setRelationMyEntitie(this.a.retrieveMethod());
em.persist(oe);
}
@TransactionAttribute(REQUIRED)
public void end(){
em.flush();
}
}
The problem come on executing em.persist(oe). oe has a reference to an instance of MyEntity which was loaded by another EntityManager. So em don't know it complaining about persisting detached Entity.
I would like to know what is there is a way to avoid this problem. If there is no direct solution, what is the best pattern to adopt ?
EDIT: I don't want to use a transaction on start() because in real application, the statefull bean is used to realize a complex entity model which need to be persist at once. I try to setup the pattern called session-per-conversation in described here http://docs.jboss.org/hibernate/core/4.0/manual/en-US/html/transactions.html#transactions-basics-apptx. So if I understand you right, the solution is to "use a transaction in start() method of bean B", but if I do this, at the end of the method, the content is flushed to database and it's not what I want.
Other solution I can see is to get MyEntity in the B's EntityManager, so do a merge, or a em.find() or to delegate retrieveMethod to some DAO style class, using the em in parameter, and in bean A, do a simple delegation to the DAO, in bean B, call directly the DAO.
Any idea on what is the best approach ?
发布评论
评论(3)
这是一个比较有趣的问题。这个设置看起来绝对合理,但困难在于事务的两个“自动”行为:
这变得更加困难因为您要共享的资源正是实体管理器。否则,您可以使用 EJB 中的第三种变体,称为“应用程序管理的”实体管理器。这个可以通过编程方式与事务关联(使用 em.join()),与业务方法是否处于事务中无关。
您要么需要在没有交易的情况下进行共享,要么阻止它们在交易关联时自动刷新。据我所知,两者都是 EJB 中缺失的功能。 也许应用程序管理的扩展em不会执行自动刷新,但我不会屏住呼吸。
但更手动的方法又如何呢?暂时不要调用 em.persist(),而是使用单独的列表来存储对对话期间需要保留的任何实体的引用。
然后在 close 方法中迭代该列表并调用 em.persist()。
Ps
另一种选择:如果使用 em.merge() 而不是 em.persist() 会怎样?合并是一种多功能方法,可以执行更新和插入操作,并且不关心实体是否附加。如果 A 和 B 之间的实体永远不会分离,那就更好了,但这可能是一个实用的解决方案。
This is a rather interesting question. The setup seems absolutely reasonable, but the difficulty is in two "automatic" behaviors of a transaction:
It's made even more difficult because of the fact that the resource you want to share is exactly the entity manager. Otherwise you could have used the third variant in EJB called the "application managed" entity manager. This one can be programmatically associated with a transaction (using em.join()), independently of the bussiness method being in transaction or not.
You would either need to share without a transaction, or prevent the em to auto-flush upon transaction association. Both are to the best of my knowledge missing features in EJB. Perhaps the application managed extended em doesn't do the auto-flush, but I wouldn't hold my breath.
But what about a more manual approach? Don't call em.persist() yet, but use a seperate list to store references to any entities that need to be persisted during the conversation.
Then in the close method itterate that list and do call em.persist().
P.s.
One other option: what if you use em.merge() instead of em.persist()? Merge is a multifunctional method that does both updates and inserts and doesn't care about entities being attached or not. It would be nicer if entities never became detached between A and B, but this might be a practical solution.
问题似乎是正常(非扩展)持久性上下文的范围仅限于 JTA 事务。
由于您已将 bean B 声明为事务
NOT_SUPPORTED
,而 A 具有REQUIRED
,因此调用方法start
将为您提供与`检索方法'。 (实际上,在第一种情况下根本不存在持久性上下文)。通常,在 EJB 中,包括实体管理器在内的资源会在单个事务中自动共享,因此即使看起来像是不同 bean 中的不同注入,您仍然会获得相同的资源。
即使没有 bean A,您的代码也无法工作,因为持久化需要存在事务。显式刷新也没有多大用处,因为在此上下文中不需要这样做(在事务提交时自动发生)。
如果您希望在会话期间保持托管实体的附加状态,Bean B 可以使用扩展持久性上下文
@PersistenceContext(type = EXTENDED)
。如果它也使用事务,那么 bean A 将共享相同的上下文,即使它本身没有扩展上下文(重要的是它将从 B 的事务上下文中调用)。The problem seems to be that a normal (non-extended) persistence context is scoped to a JTA transaction.
Since you have declared bean B as transactions
NOT_SUPPORTED
, while A hasREQUIRED
, invoking methodstart
will give you a different persistence context as the one in `retrieveMethod'. (actually, there will be not persistence context at all in the first case).Normally in EJB resources, including entity managers are automatically shared within a single transaction, so even if looks like different injections in different beans, you'll still get the same resource.
Even without bean A, your code would not have worked since persisting requires a transaction to be present. The explicit flush wouldn't be of much use either, since this isn't needed in this context (happens automatically when the transaction commits).
If you want to keep the managed entities attached during the conversation, bean B can use the extended persistence context
@PersistenceContext(type = EXTENDED)
. If it also uses transactions, then bean A will share the same context, even though it itself does not have an extended context (what matters is that it will be called from within B's transactional context).这是我使用的解决方案:
我希望它能有所帮助(并且我希望这是一个不那么难看/愚蠢的解决方案)。
Here is the solution I used :
I hope it can help (and I hope this a not so ugly/dumb solution).