Search code examples
jpajsf-2transactionsjta

Making sure JPA commits my transaction before another service accesses the data


It feels so simple:

I have a ViewScoped bean (JPA2 + EE6 + Seam3, if that matters) where the user of the web application can invoke a method like this:

public void save() {
    doEntityManagerStuff(); // manipulates data in the database
    callRemoteWebservice(); // which is to read said data and propagate it to other systems
}

Unfortunately, save() starts a transaction at the opening curly bracket and doesn't commit it before the closing bracket, meaning that the new data is not available to the remote web service to read.

I have tried to explicitly extract and annotate the database work:

@TransactionAttribute(REQUIRES_NEW)
private void doEntityManagerStuff() {
    blabla(); // database stuff
}

But that didn't have any impact at all. (Maybe because that's EJB stuff and I'm running on seam...?)

The only thing that worked for me so far was to inject @UserTransaction and force commit the transaction at the end of either save() or doEntityManagerStuff() but that felt incredibly dirty and dangerous.

The other alternative would be to turn off container-managed transactions for the entire project, but that means I'd have to make all my beans manage their transactions manually, just so I can make this one case work.

Is there a better way?


Solution

  • To answer my own question:

    I only went half-way, and that's why it didn't work. I didn't know enough about EJBs and their boudaries, and naively though just annotating the doEntityManagerStuff(...) method with a transaction attribute in my view-scoped CDI/Seam bean would be enough.

    It isn't.

    When I moved said method into a separate, stateless EJB, injected that into my CDI/Seam bean and called it from there, everything worked as expected.


    @Stateless
    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    public class MyPersister {
        ...
    
        public void doEntityManagerStuff() {
            blabla(); // database stuff
        }
    
        ...
    }
    

    and

    @ViewScoped
    public class MyWebsiteBean {
        ...
        @Inject MyPersister persister;
        ...
    
        public void save() {
            persister.doEntityManagerStuff(); //uses its own transaction
            callRemoteWebService();
        }
    
        ...
    }