Commit transaction only if all EJBs succeed

-1

I have a complex problem, but one that can be exemplified through the following metaphor:

@Stateless
public interface ChildEJB01 {
    salvarGato(Gato g);
}

@Stateless
public interface ChildEJB01 {
    salvarCachorro(Cachorro c);
}

@Stateless
public interface ChildEJB01 {
    salvarPeriquito(Periquito p);
}

These three EJBs are used in isolation at various points in my application and work well in isolation. But there is a certain time that I need to use them together, see:

@Statefull
public class MasterEJB {

    @EJB
    private ChildEJB01 a;

    @EJB
    private ChildEJB02 b;

    @EJB
    private ChildEJB01 c;

    public void salvarTudo(){
        a.salvarGato();
        b.salvarCachorro();
        c.salvarPeriquito();
    }

}

Now I need to save all at once and I need everything saved in the database only if all are saved correctly [transaction].

But I have noticed that even if one of them gives error what was done previously is saved anyway ... How do I create a transaction for this method? (I'm using JTA (managed by Container)).

My setup: Jboss AS7

editing for a new example using Ricardo's tips

@Stateless
@TransactionManagement(TransactionManagementType.CONTAINER)
public class ChildEJB01 implements C01 {

@PersistenceContext
private EntityManager manager;

@WebMethod
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void persist(String a){
    Model m = new Model();
    m.setDesc(getClass().getCanonicalName());

    manager.persist(m);
}

}

@Stateless
@TransactionManagement(TransactionManagementType.CONTAINER) 
public class ChildEJB02 implements C02 {

@PersistenceContext
private EntityManager manager;

@WebMethod
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void persist(String a){
    Model m = new Model();
    m.setDesc(getClass().getCanonicalName());

    manager.persist(m);
}

}

@WebService
@Stateless
@TransactionManagement(TransactionManagementType.CONTAINER)
public class MasterEJB {

@EJB
private C01 c01;

@EJB
private C02 c02;

@Resource
private SessionContext contexto;

@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void persist() {
    for (int i = 0; i < 3; i++) {
        c01.persist("");
        c02.persist("");

        if (i == 2) // forcando uma exceção apos 4 inserts
            throw new RuntimeException();
    }
}

}

SELECT THE BANK BEFORE ROLLING THE CODE:

  

mysql > SELECT * FROM test;   Empty set (0.00 sec)   mysql >

CONSOLE OUTPUT DURING CODE EXECUTION:

  Hibernate: insert into test (description) values (?)

10: 26: 00,905 INFO [stdout] (http -      Hibernate: insert into test (description) values (?)

10: 26: 01,006 INFO [stdout] (http - 0.0.0.0-8080-1)      Hibernate: insert into test (description) values (?)

10: 26: 01,057 INFO [stdout] (http - 0.0.0.0-8080-1)      Hibernate: insert into test (description) values (?)

10: 26: 01,101 INFO [stdout] (http - 0.0.0.0-8080-1)      Hibernate: insert into test (description) values (?)

10: 26: 01,135 INFO [stdout] (http - 0.0.0.0-8080-1)      Hibernate: insert into test (description) values (?)

10: 26: 01,168 INFO [stdout] (http - 0.0.0.0-8080-1)      

10: 26: 01,209 ERROR [org.jboss.ejb3.invocation] (http - 0.0.0.0-8080-1) JBAS014134: EJB Invocation failed on component MasterEJB for method public void implementacoes.MasterEJB.persist (): javax.ejb.EJBException: java.lang.RuntimeException

SELECT NO BANK:

  

mysql > SELECT * FROM test;

     

+ ---- + --------------------------- +

     

| id | description |

     

+ ---- + --------------------------- +

     

| 1 | implementacoes.ChildEJB01 |

     

| 2 | implementacoes.ChildEJB02 |

     

| 3 | implementacoes.ChildEJB01 |

     

| 4 | implementacoes.ChildEJB02 |

     

| 5 | implementacoes.ChildEJB01 |

     

| 6 | implementacoes.ChildEJB02 |

     

+ ---- + --------------------------- +

     

6 rows in set (0.00 sec)

===

In short .. rollback still does not work: (

== +1 edit:

now using a single EJB (no other injected EJBs are used);

@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void persist() {

    for (int i = 0; i < 5; i++) {
        Model m = new Model();
        m.setDesc("KEKEKEKEKKE: " + i);
        manager.persist(m);
    }

    contexto.setRollbackOnly();
}

The rollback was not done .. it's VERY STRANGE: o

RESOLVED:

I needed to activate the transactions for my JBOSS DATASOURCE .. it was only to mark the box: [x] USE JTA

    
asked by anonymous 27.03.2014 / 22:15

1 answer

2
Come on! This will require a little knowledge in transactions and the propagation of them. In addition to enabling JTA usage in persistence.xml.

Changing persistence.xml

To enable transactions via JTA , you must - specify in which datasource the transaction will occur, as follows:

<jta-data-source>java:/meuDatasource</jta-data-source>

Noting the EJB's

Now we need to note the child EJB's methods to support TransactionAttributeType.REQUIRED propagation. This means that:

  

TransactionAttributeType.REQUIRED: If a transaction is already in progress, then the same transaction will be used. If there is no transaction in progress, then a new one will be created.

Let's go to the codes:

ChildEJB1

@Stateless
public class ChildEJB1 {

    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void salvarPiriquito() {
        //codigo
    }
}

ChildEJB2

@Stateless
public class ChildEJB2 {

    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void salvarGato() {
        // codigo
    }
}

ChildEJB3 :

@Stateless
public class ChildEJB3 {

    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void salvarCachorro() {
        //codigo
    }
}

And finally, MasterEJB :

@Stateful
public class MasterEJB {

    private ChildEJB1 ejb1;
    private ChildEJB2 ejb2;
    private ChildEJB3 ejb3;

    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    public void salvarTudo() {
        this.ejb1.salvarPiriquito();
        this.ejb2.salvarGato();
        this.ejb3.salvarCachorro();
    }
}

Note that in MasterEJB I used another type of transaction propagation ( TransactionAttributeType.REQUIRES_NEW ). What this type of propagation tells us:

  

TransactionAttributeType.REQUIRES_NEW: This propagation mandatorily creates a new transaction from a context without any transactions. If a transaction is already in progress, then an error will be generated.

And what does all this code posted mean?

It means that when you call the "child" EJB methods in isolation, a new transaction will be created and the objects will be saved. I am assuming that when these methods are called "isolated", has no previously open transactions.

When child EJB's methods are called by the salvarTudo() method, you see that in the salvarTudo() method the transaction is already opened and this transaction propagates to child EJB methods . If the transaction fails on any child EJB method, then the rollback will be done and no object will persist from any child EJB.

    
28.03.2014 / 13:57