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