Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Retrieve Entities throught stateless local EJB from statefull EJB (Long conversation using session-per-conversation)

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 ?

like image 664
Yann Le Moigne Avatar asked Dec 14 '25 16:12

Yann Le Moigne


1 Answers

This is a rather interesting question. The setup seems absolutely reasonable, but the difficulty is in two "automatic" behaviors of a transaction:

  • Share resources to other beans in the invocation chain
  • Automatically persist operations that were queued when the persistence context was not associated with 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.

like image 196
Mike Braun Avatar answered Dec 17 '25 05:12

Mike Braun



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!