Use JTA transaction with EJB and JPA - jpa

Hello everybody i'm looking for some help with the next problem:
i have a jpa/stateless ejb's proyect that works perfectly, it just does simple queries and persist operations, but now i need to execute a set of persist operations, if any of them fails, i must perform a rollback, so i found JTA can do the job but using this piece of source code:
#Stateless
public class ProjectBean implements IProject {
#Resource
javax.transaction.UserTransaction utx;
#PersistenceContext(unitName = "JPADB")
private EntityManager entityManager;
...
//more code
//this is part of a method
try{
utx.begin();
entityManager.joinTransaction();
for(Project p:projectResultList){
entityManager.persist(p);
}
utx.commit();
}catch(Exception e){
e.printStackTrace();
if(utx != null)
try {
utx.rollback();
} catch (IllegalStateException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (SecurityException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (SystemException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
//tx.rollback();
}
simply doesn't work, and this is how the persistence.xml looks like:
<persistence-unit name="JPADB">
<jta-data-source>java:jboss/datasources/OracleBic</jta-data-source>
<properties>
<property name="hibernate.show_sql" value ="true" />
<property name="hibernate.dialect" value="org.hibernate.dialect.Oracle10gDialect" />
</properties>
</persistence-unit>
really hope anyone can give me a tip or advice, i'm a newbie with jpa/jta concepts and i tried lots of codes i found in the web but i always obtain different errors (Wrong tx on thread: expected TransactionImple usertransaction begin,Cannot use an EntityTransaction while using JTA). thanks in advance.

Did you instruct your AS that you are going to handle transactions manually with
#TransactionManagement(TransactionManagementType.BEAN) on method level? I do not see the annotation on class level. Probably you have it on method level, but your code snipped is insufficient to make any guess.
Otherwise all transactions are Container managed and your code is not going to work. So you have to either put
#TransactionManagement(TransactionManagementType.BEAN)
on either a method or class level depends on requirements or you may want to allow your container to manage transactions for you and than you have to make changes that #remigio suggested to you.
It seems that the second approach is better in your case

remigio's comment is correct, #Stateless session beans control transaction boundaries with javax.ejb.#TransactionAttribute, if the annotation is absent all public methods are TransactionAttribute.REQUIRED. For more information, see http://download.oracle.com/otndocs/jcp/ejb-3_0-fr-eval-oth-JSpec/ (ejb-3_0-fr-spec-ejbcore.pdf).
UserTransaction is never used in a #Stateless session bean, but rather is used by clients that are calling the bean to demarcate a wider transaction window than that of just the method call itself.

Related

Using Nativequery to delete/update records in JTA

I was stuck in very hard situation for a moment.
The problem is: I have to use NativeQuery to delete and/or update some DB records in a JTA context (EJB).
my JPA persistence.xml looks like:
<persistence-unit name="OzsscJPANBPU" transaction-type="JTA">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>jdbc/postgres_ozssc</jta-data-source>
<mapping-file>com/longz/ozssc/model/PostcodeEntity.xml</mapping-file>
<class>com.longz.ozssc.model.CustomerEntity</class>
......
If I use delete statement in this way:
#Override
public void remove(SmsdOutboxEntity toberemoved){
em.createNativeQuery("Delete from outbox where \"ID\" = " + toberemoved.getId()).executeUpdate();
TransactionRequiredException thrown:
root cause
javax.persistence.TransactionRequiredException:
Exception Description: No transaction is currently active
Such there is no transaction existing.
If we use transaction manually as:
#Override
public void remove(SmsdOutboxEntity toberemoved){
em.getTransaction().begin();
em.createNativeQuery("Delete from outbox where \"ID\" = " + toberemoved.getId()).executeUpdate();
/*em.flush();*/
em.getTransaction().commit();
IllegalStateException thrown:
root cause
javax.ejb.EJBException: EJB Exception: ; nested exception is:
java.lang.IllegalStateException: The method public abstract javax.persistence.EntityTransaction javax.persistence.EntityManager.getTransaction() cannot be invoked in the context of a JTA EntityManager.
Seems I can't use transaction manually, as JTA will manage transaction their self.
So my question is: How I can use Native Query to delete/update records in a JTA managed context?
Please advise.
For JTA the transaction is provided by the container and you should not create it by yourself. To obtain the transaction in JTA, you have to access it as a resource:
#Resource public UserTransaction utx;
#Resource public EntityManagerFactory factory;
#Override
public void remove(SmsdOutboxEntity toberemoved){
EntityManager em = factory.createEntityManager();
try {
em.createNativeQuery("Delete from outbox where \"ID\" = " + toberemoved.getId()).executeUpdate();
utx.commit();
catch (RuntimeException e) {
if (utx != null) utx.rollback();
throw e; // or display error message
}
finally {
em.close();
}
If you want to omit the part where you have commit/rollback the transaction by yourself, you need to have your transaction managed by some container like EJB3. For that you can use stateless/stateful ejb beans.
UPDATE:
For WebLogic, try to use its user weblogic.transaction.UserTransaction instead of javax.transaction.UserTransaction. As the doc says:
This interface defines WebLogic-specific extensions to
javax.transaction.UserTransaction.

Is it possible to annotate a method with #TransactionAttribute in BMT EJB?

I came across a piece of code where the bean implementation class has #TransactionManagement(TransactionManagementType.BEAN) annotation wherein methods are annotated with CMT #TransactionAttribute. Is is valid?
Can an EJB with BMT persistence use CMT transaction annotation? What will be the behavior at runtime?
Though javadoc http://docs.oracle.com/javaee/6/api/javax/ejb/TransactionAttribute.html says that "It can only be specified if container managed transaction demarcation is used.", specifying it doesn't throw any compilation error. Does it mean that jvm simply ignores it at runtime?
#Stateless( mappedName = "Abc")
#Remote("AbcRemote.class")
#Local("AbcLocal.class")
#TransactionManagement(TransactionManagementType.BEAN)
public class AbcBean implements AbcLocal, AbcRemote{
#Resource
private UserTransaction utx;
#PersistenceUnit
private EntityManagerFactory emf;
#Override
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public Abc getAlpbabets(String name) {
EntityManager em = null;
try {
em = emf.createEntityManager();
}
catch (RuntimeException re) {
throw re;
}
finally {
}
}
}
If you use CMT, then #TransactionAttribute(TransactionAttributeType.REQUIRED) would tell the container to check for an existing transaction and open one if there is none.
But if you use BMT, then it's your responsibility to do such a thing, so there's no one to observe the annotation above. Since it is still syntactically correct and the class is available, there is no need for the JVM to complain about.
Concerning ignoring annotations, there's a hint in the answer to this question.

How to propagate a client-side UserTransaction into a stateless session bean using BMT

This scenario using CMT is working:
Stateless session bean with CMT, one method annotated with #TransactionAttribute(TransactionAttributeType.MANDATORY). Within this method, a record is written into a RDBMS using an XA data source and plain JDBC.
The stand-alone client (separate JVM, command-line Java application) is getting a UserTransaction from the application server (by JNDI lookup),
starts the transaction, and calls the EJB.
If the client commits the UserTransaction, the record is written into the database.
If the client rollbacks the UserTransaction, the record is not written into the database.
In the PostgreSql log files, one can see the prepared transaction with BEGIN, and COMMIT or ROLLBACK
If the client does not start a transaction before calling the EJB, a javax.ejb.EJBTransactionRequiredException is thrown (as expected, TransactionAttributeType.MANDATORY).
Now I switch from CMT to BMT
Again, if the client does not start a transaction before calling the EJB, a javax.ejb.EJBTransactionRequiredException is thrown (as expected, TransactionAttributeType.MANDATORY).
If I call sessionContext.getUserTransaction().getStatus(), it always reports Status.STATUS_NO_TRANSACTION.
The record is always written into the database, if the client calls commit or rollback.
In the PostgreSql log files, there are no prepared transactions, just plain insert commands.
The source of the EJB:
#Remote(DemoIfc.class)
#Stateless(name = "DemoBmt")
#TransactionManagement(TransactionManagementType.BEAN)
public class DemoBmt implements DemoIfc {
#Resource
private SessionContext sessionContext;
#TransactionAttribute(TransactionAttributeType.MANDATORY)
public String ping(final String s) throws SystemException {
try {
System.out.println("TX: status: "
+ this.sessionContext.getUserTransaction().getStatus());
} catch (Exception e) {
System.out.println("TX: status: " + e.getMessage());
}
try {
writeIntoDb();
if (s.startsWith("crash")) {
throw new SystemException("Simulated crash");
}
return s.toUpperCase();
} catch (NamingException e) {
throw new SystemException(e.getMessage());
} catch (SQLException e) {
throw new SystemException(e.getMessage());
}
}
}
The client's source:
final UserTransaction ut = (UserTransaction) initialContext
.lookup("UserTransaction");
try {
ut.begin();
System.out.println(demo.ping("crash: DemoBmt with UT"));
ut.commit();
} catch (Exception ex) {
System.out.println("Expected rollback");
ut.rollback();
}
I am using JBoss 6.0.0 final.
How can I properly propagate the client-side UserTransaction into the EJB with BMT?
BMT beans cannot participate in an existing transaction
From EJB 3.1 spec.:
13.6.1 Bean-Managed Transaction Demarcation
The container must manage client invocations to an enterprise bean
instance with bean-managed transaction demarcation as follows. When a
client invokes a business method via one of the enterprise bean’s
client views, the container suspends any transaction that may be
associated with the client request....

Why transaction can't commit in a self-invoked ejb method with #REQUIRES_NEW Annotation

First I want to explain my self-invoked ejb method in this situation. I have a stateful session bean with a method which starts a new transaction (Annotated by #REQUIRES_NEW). To invoke this method inside the bean itself and make the annotation effective, I use SessionContext#getBusinessObject() to achieve the effect of #EJB (#EJB here causes stackoverflow?!). My code is shown below:
#Stateful
#Local
public class TransactionTest implements ITransactionTest {
#PersistenceContext(unitName="Table",Type=PersistenceContextType.EXTENDED)
private EntityManager manager;
#Resource
SessionContext sc;
ITransactionTest me;
#PostConstruct
public void init(){
me = this.sc.getBusinessObject(ITransactionTest.class);
}
public void generateRecord(int i) throws RuntimeException{
Record record = new Record();
record.setId(i+"");
record.status(1);
manager.persist(record);
manager.flush(); //If not flush, result is correct. Why?
me.updateRecord(i);
}
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void updateRecord(int i) throws RuntimeException{
try {
Record record = manager.find(Record.class, i+"");
record.setStatus(2);
manager.flush();
} catch(Exception e) {
e.printStackTrace();
throw new RuntimeException();
}
}
}
While,generateRecord() runs properly. The console shows it executes 'insert' and 'update' HQL without any exception (I use Hibernate as JPA provider). However, the 'update' result doesn't appear in the database. Why? Does updateRecord() commit correctly?
Also, I try it in two altenative ways: First is invoking generateRecord() (it will no longer invoke updateRecord()) and updateRecord() consecutively in another bean. It can give me the right result.
The second is removing the first flush(). Then both 'insert' and 'update' HQL will be executed at the second flush(). This method can also produce right result.
My program is running under JBOSS 6.1.0-Final and database is Oracle.
Best Regards,
Kajelas

Transaction aware objects in stateless EJB

I'm little confused how transactions work in EJBs. I've always thought that all transaction aware objects in container managed EJBs are all committed or rollbacked when a method with TransactionAttribute=REQUIRED_NEW is finished but unfortunately it's not in my case. I don't have my code in front of me so I can't include whole example but what I ask for is just the confirmation of the idea of how it should work.
Only key points of my code just from the top of my head are presented:
EntityManager em; //injected
[...]
public void someEJBMethod() {
[...]
em.persist(someObject);
[...]
Session session = JpaHelper.getEntityManager(em).getActiveSession();
[...]
session.executeQuery(query, args);
[...]
if (someCondition) {
throw new EJBException();
}
[...]
}
And my problem is that when EJBException is thrown database changes caused by em.persist are rollbacked but changes caused by session.executeQuery are committed.
Is it expected behaviour?
I'm using Glassfish 3.1.2, EclipseLink 2.3.2 with Oracle database
Update (test case added)
I've created working test case to show the problem
First database objects:
create table txtest
(id number not null primary key,
name varchar2(50) not null);
create or replace function txtest_create(p_id number, p_name varchar2) return number is
begin
insert into txtest
(id, name)
values
(p_id, p_name);
return p_id;
end;
Definition of a database connection (from domain.xml)
<jdbc-connection-pool driver-classname="" datasource-classname="oracle.jdbc.pool.OracleConnectionPoolDataSource" res-type="javax.sql.ConnectionPoolDataSource" description="" name="TxTest">
<property name="User" value="rx"></property>
<property name="Password" value="rx"></property>
<property name="URL" value="jdbc:oracle:thin:#test:1529:test"></property>
</jdbc-connection-pool>
<jdbc-resource pool-name="TxTest" description="" jndi-name="jdbc/TxTest"></jdbc-resource>
persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="txTest">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>jdbc/TxTest</jta-data-source>
<class>txtest.TxTest</class>
</persistence-unit>
</persistence>
session bean:
#Stateless
public class TxTestBean implements TxTestBeanRemote, TxTestBeanLocal {
private static Logger log = Logger.getLogger(TxTestBean.class.getName());
#PersistenceContext(unitName="txTest")
EntityManager em;
#SuppressWarnings({ "unchecked", "rawtypes" })
#Override
public void txTest(boolean throwException) {
TxTest t = new TxTest();
t.setId(1L);
t.setName("em.persist");
em.persist(t);
Session session = JpaHelper.getEntityManager(em).getActiveSession();
log.info("session : " + String.valueOf(System.identityHashCode(session)));
PLSQLStoredFunctionCall call = new PLSQLStoredFunctionCall();
call.setProcedureName("txtest_create");
call.addNamedArgument("p_id", JDBCTypes.NUMERIC_TYPE);
call.addNamedArgument("p_name", JDBCTypes.VARCHAR_TYPE, 50);
call.setResult(JDBCTypes.NUMERIC_TYPE);
ValueReadQuery query = new ValueReadQuery();
query.setCall(call);
query.addArgument("p_id");
query.addArgument("p_name");
t = new TxTest();
t.setId(2L);
t.setName("session.executeQuery");
List args = new ArrayList();
args.add(t.getId());
args.add(t.getName());
Long result = ((Number)session.executeQuery(query, args)).longValue();
//added to see the state of txtest table in the database before exception is thrown
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("result=" + result.toString());
if (throwException) {
throw new EJBException("Test error #1");
}
}
}
entries from server.log when txTest(true) is invoked:
[#|2012-05-21T12:04:15.361+0200|FINER|glassfish3.1.2|org.eclipse.persistence.session.file://txTest/_txTest.connection|_ThreadID=167;_ThreadName=Thread-2;ClassName=null;MethodName=null;|client acquired: 21069550|#]
[#|2012-05-21T12:04:15.362+0200|FINER|glassfish3.1.2|org.eclipse.persistence.session.file://txTest/_txTest.transaction|_ThreadID=167;_ThreadName=Thread-2;ClassName=null;MethodName=null;|TX binding to tx mgr, status=STATUS_ACTIVE|#]
[#|2012-05-21T12:04:15.362+0200|FINER|glassfish3.1.2|org.eclipse.persistence.session.file://txTest/_txTest.transaction|_ThreadID=167;_ThreadName=Thread-2;ClassName=null;MethodName=null;|acquire unit of work: 16022663|#]
[#|2012-05-21T12:04:15.362+0200|FINEST|glassfish3.1.2|org.eclipse.persistence.session.file://txTest/_txTest.transaction|_ThreadID=167;_ThreadName=Thread-2;ClassName=null;MethodName=null;|persist() operation called on: txtest.TxTest#11b9605.|#]
[#|2012-05-21T12:04:15.363+0200|INFO|glassfish3.1.2|txtest.TxTestBean|_ThreadID=167;_ThreadName=Thread-2;|session : 16022663|#]
[#|2012-05-21T12:04:15.364+0200|FINEST|glassfish3.1.2|org.eclipse.persistence.session.file://txTest/_txTest.query|_ThreadID=167;_ThreadName=Thread-2;ClassName=null;MethodName=null;|Execute query ValueReadQuery()|#]
[#|2012-05-21T12:04:15.364+0200|FINEST|glassfish3.1.2|org.eclipse.persistence.session.file://txTest/_txTest.connection|_ThreadID=167;_ThreadName=Thread-2;ClassName=null;MethodName=null;|Connection acquired from connection pool [read].|#]
[#|2012-05-21T12:04:15.364+0200|FINEST|glassfish3.1.2|org.eclipse.persistence.session.file://txTest/_txTest.connection|_ThreadID=167;_ThreadName=Thread-2;ClassName=null;MethodName=null;|reconnecting to external connection pool|#]
[#|2012-05-21T12:04:15.365+0200|FINE|glassfish3.1.2|org.eclipse.persistence.session.file://txTest/_txTest.sql|_ThreadID=167;_ThreadName=Thread-2;ClassName=null;MethodName=null;|
DECLARE
p_id_TARGET NUMERIC := :1;
p_name_TARGET VARCHAR(50) := :2;
RESULT_TARGET NUMERIC;
BEGIN
RESULT_TARGET := txtest_create(p_id=>p_id_TARGET, p_name=>p_name_TARGET);
:3 := RESULT_TARGET;
END;
bind => [:1 => 2, :2 => session.executeQuery, RESULT => :3]|#]
[#|2012-05-21T12:04:15.370+0200|FINEST|glassfish3.1.2|org.eclipse.persistence.session.file://txTest/_txTest.connection|_ThreadID=167;_ThreadName=Thread-2;ClassName=null;MethodName=null;|Connection released to connection pool [read].|#]
[#|2012-05-21T12:04:35.372+0200|INFO|glassfish3.1.2|txtest.TxTestBean|_ThreadID=167;_ThreadName=Thread-2;|result=2|#]
[#|2012-05-21T12:04:35.372+0200|FINER|glassfish3.1.2|org.eclipse.persistence.session.file://txTest/_txTest.transaction|_ThreadID=167;_ThreadName=Thread-2;ClassName=null;MethodName=null;|TX afterCompletion callback, status=ROLLEDBACK|#]
[#|2012-05-21T12:04:35.372+0200|FINER|glassfish3.1.2|org.eclipse.persistence.session.file://txTest/_txTest.transaction|_ThreadID=167;_ThreadName=Thread-2;ClassName=null;MethodName=null;|release unit of work|#]
[#|2012-05-21T12:04:35.372+0200|FINER|glassfish3.1.2|org.eclipse.persistence.session.file://txTest/_txTest.connection|_ThreadID=167;_ThreadName=Thread-2;ClassName=null;MethodName=null;|client released|#]
[#|2012-05-21T12:04:35.373+0200|WARNING|glassfish3.1.2|javax.enterprise.system.container.ejb.com.sun.ejb.containers|_ThreadID=167;_ThreadName=Thread-2;|EJB5184:A system exception occurred during an invocation on EJB TxTestBean, method: public void txtest.TxTestBean.txTest(boolean)|#]
[#|2012-05-21T12:04:35.373+0200|WARNING|glassfish3.1.2|javax.enterprise.system.container.ejb.com.sun.ejb.containers|_ThreadID=167;_ThreadName=Thread-2;|javax.ejb.EJBException: Test error #1
What surprised me the most is that when I checked txtest table during this 20 sec. sleep the record (2,"session.executeQuery") was already there.
It seems like session.executeQuery somehow commits its work (but not the whole transaction).
Can someone explain this behaviour?
I'm not sure what JpaHelper.getEntityManager(em).getActiveSession(); is supposed to do exactly, but it seems likely this doesn't return a container managed entity manager. Depending on how it's exactly implemented, this may not participate in the ongoing (JTA) transaction.
Normally though, transactional resources all automatically participate in the ongoing JTA transaction. In broad lines they do this by checking if there's such on ongoing transaction, and if there indeed is, they register themselves with this transaction.
In EJB, REQUIRES_NEW is not the only mode that can start a transaction 'REQUIRES' (the default) also does this incase the client didn't start a transaction.
I've got it solved!!!
It turned out that EclipseLink uses read connection pools for processing read queries (and obviously that kind of pools uses autocommit or even don't use transactions at all) and default connection pools for data modification queries. So what I had to do was changing:
ValueReadQuery query = new ValueReadQuery();
into
DataModifyQuery query = new DataModifyQuery();
and it works like a charm.
Update
DataModifyQuery doesn't allow to get the result of the function. It returns the number of modified rows. So I got back to ValueReadQuery but used configuration parameter in persistance.xml
<property name="eclipselink.jdbc.exclusive-connection.mode" value="Always"/>
This parameter tells EclipseLink to use default connection pool for both reads and writes.