This week's book giveaway is in the Servlets forum.
We're giving away four copies of Murach's Java Servlets and JSP and have Joel Murach on-line!
See this thread for details.
The moose likes EJB and other Java EE Technologies and the fly likes Problem with ejb transaction Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login


Win a copy of Murach's Java Servlets and JSP this week in the Servlets forum!
JavaRanch » Java Forums » Java » EJB and other Java EE Technologies
Bookmark "Problem with ejb transaction" Watch "Problem with ejb transaction" New topic
Author

Problem with ejb transaction

Rashid Darvesh
Ranch Hand

Joined: Feb 13, 2004
Posts: 189
Hi
i am trying to test the Container Managed tranaction and the problem is that the part of hte transaction succeeds and part of the transaction fails. As you can see below i have two method saveCustomer and saveCustomerLog whose transaction state is defined as Required. On the ejb side when i call the saveCustomer method i get a datasource by looking at the JNDI and do some insertion in that table and close the statement and connection.(Note: i dont do explicit commit here with connection object). then i call again the method saveCustomerLog on the same bean again get the datasource and do some insertion in some other table. The problem is that i wantedly put some error in the second call so that the entire transaction should fail but the first goes fine and the second part fails.
Here is the descriptor for the transaction part
<assembly-descriptor>
<container-transaction>
<method>
<ejb-name>CustomerTrans</ejb-name>
<method-name>saveCustomer</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
<container-transaction>
<method>
<ejb-name>CustomerTrans</ejb-name>
<method-name>saveCustomerLog</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
</assembly-descriptor>

Please let me know if i am missing anything
Thanks in advance

Rashid
Murali Pen
Greenhorn

Joined: May 23, 2006
Posts: 28
I assume you are calling these 2 methods on the CustomerTrans EJB from a client which invokes these 2 methods from its own transaction context? Is that correct?

Is CustomerTrans EJB a session (stateless/stateful) or entity bean?


Murali
SCJP,SCBCD,SCWCD
Purushoth Thambu
Ranch Hand

Joined: May 24, 2003
Posts: 425
If you want transactions to span multiple methods then You should use the same connection. Once the connection is retrieved the same connection must be used for the entire transaction.
Rashid Darvesh
Ranch Hand

Joined: Feb 13, 2004
Posts: 189
I thought the whole point of EJB was you can get a conection execute one mehotod close it and then open again one connection and execute one method. The only thing you should not do is doing an explict commit on a connection. Please let me know if this is incorrect. As for Murali question
i get the
Customer cust = (Customer)custHome.create();
then i use the cust object to call this two methods
cust.saveCustomer(...);
cust.saveCustomerLog(...)

the saveCustomer method ejb will grab a connection from the datasource insert some records and close the connection (Again i dont make an explicti call for commmit) Again the saveCustomerLog method gets a connection from the datasource inserts some records in some other table and close the connection. i dont have the entire code as of now with me since the code is tehre at my office computer. But my question do i need to use the same connection throught out the code since i think how can u keep the same connection for example if i have to write down to two databases which are totally different. in that case it might be totally impossible to define transaction.

Please let me know
Rashid
Purushoth Thambu
Ranch Hand

Joined: May 24, 2003
Posts: 425
Take a look at example provided in EJB 2.1 Specification page# 369. This example shows how to handle transaction that spans multiple methods
Murali Pen
Greenhorn

Joined: May 23, 2006
Posts: 28
You haven't answered my question about the type of bean you are using and also about the client transaction context. Well, let's consider different scenarios with CMT.

Assuming you are using a session bean S1 with methods m1 and m2, each with transaction attribute set as Required.

Scenario1:

Client starts transaction
Invokes m1 on S1
Invokes m2 on S1
Client ends transaction

Here, the methods m1 and m2 are executed in the client's transaction context. If m2 fails for some reason, the entire transaction fails.

Scenario2:

Client doesn't start any transaction
Invokes m1 on S1
Invokes m2 on S1

Here, each method starts its own transaction and ends when each method completes execution. So if m2 fails for any reason, m1 transaction is not rolled back.

You sound like you are testing the scenario2 I described above.

To address your other question, you cannot reuse the same database connection object across multiple methods for stateless beans. This is because they are by definition stateless and reusing the connection requires you to save the state across method calls. Since stateless session beans are returned to the pool after each method execution you cannot do this. It is possible to reuse the connection with Stateful session beans.

I hope this clarifies things. Let me know.
[ June 12, 2006: Message edited by: Murali Pen ]
Rashid Darvesh
Ranch Hand

Joined: Feb 13, 2004
Posts: 189
Thanks a lot Murali. I really appreciate it.
You are correct i am using scenario 2 where i dont start my user transaction.
I am using stateful session bean so its the same bean which calls the two method. But i am not using the same connection for the two methods. The first method gets a connection from the datasource executes it and then closes the connection. the second method again opens a new connection from the datasource and does insertion on some other tables. what looks like from our discussion is that you have to use the same connection for both the methods. if thats the case consider this scenario. what if method method(m1) writes to some databse and method(m2) writes to some other databse in this case we have to use two different connection. How do u handle this, coz isn't it the catch of EJB that it use transaction boundary which covers different databases. If i have to use the same connection with out closing it then i can always use the basic connection.beginTrnasaction and connection.commit() or connection.rollback() if htere is any error. Please let me know. Please note that i am a pretty starter in EJB, so please bear with me if my questions are pretty simple.
Rashid Darvesh
Ranch Hand

Joined: Feb 13, 2004
Posts: 189
Sorry i forgot to paste the code Here it goes
CustomerHome custHome= (CustomerHome)context.lookup("CustomerTrans");
Customer cust = custHome.create();
cust.saveCustomer("C3","some first","somelast","somedate","N");
cust.saveCustomerLog("C3","some first","somelast");
Pleaselet knwo what should i add. the other thing is i defined my methods to be container managed in the ejb-jar.xml. So why do u think i need to start hte user transaction.
Please let meknow
Thanks
Rashid
Murali Pen
Greenhorn

Joined: May 23, 2006
Posts: 28
You are confused about many things. Let's go step by step.

1) There is no rule that says you have to reuse the DB connection with stateful session beans. You can close the connection in one method and
open a new one in another method, even if you are connecting to the same database. It is entirely up to you. Having said that, it is more
efficient to reuse the existing connection instead of closing and opening it again. If you are connecting to 2 different databases, of course
you will need to create 2 different connections.

2) When you are using CMT, you cannot use resource manager transactions. The list of prohibited methods are

* commit, setAutoCommit and rollback methods of java.sql.connection.
* getUserTransaction method of javax.ejb.EJBContext
* Any method of javax.transaction.UserTransaction

3) You are probably confusing transactions with statefulness. If your client does not start a user transaction and invokes 2 CMT methods on an EJB (with transaction attribute - required), any state information is definitely maintained in the EJB across method calls but it doesn't mean both the methods run in the same transaction scope. The transaction's scope for each method (with required attribute) ends when the method ends.

I never tested this scenario and that's how I believe it should behave. So, in order to maintain transaction scope across method calls, you can try 2 things.

a) Don't start a user transaction in the client. Invoke method2 from method1.
b) Start a user transaction in the client. Invoke method1 and method2 from the client's user transaction.

Post your results after you tried the above 2 things.

4) Code changes

Client Code changes:

UserTransaction ut = context.getUserTransaction();

try{

ut.begin();
//your EJB calls here
ut.commit();
}
catch(Exception e){

try{

ut.rollback();
} catch(SystemException se){

throw new EJBException("Rollback failed!" + se.getMessage() )
}

throw new EJBException("transaction failed!" + e.getMessage() )

}

Changes to EJB methods:

* cleanup your EJB methods by removing resource manager transaction code.
* In your second method, where you make the update fail, catch the SQLException and from the catch block call setRollbackOnly() on the EJBcontext(Or don't call this method and just throw an EJBException from this catch block).
Rashid Darvesh
Ranch Hand

Joined: Feb 13, 2004
Posts: 189
As you said i invoked saveCustomerLog from saveCustomer. Since i gave u the client code, i am posting the entire source code since i want this to nail it down and dont want to waste much of your time. The only thing i doubt is maybe i am not using XA compliant datbase driver. But i ensure that thing also. i am using weblogic and when i configure a connection pool i selected this driver which is XA complaint (oracle.jdbc.xa.client.OracleXADataSource) and then when i created the datasource i checked the Emulate Two-Phase Commit for non-XA Driver. May be i need to know more about from the Weblogic server side what needs to be done more. Do you think this might be the case.
this is my ejb code which does this
public void saveCustomer(String custID,String fname,String lname,String create_Date,String is_deleted){
System.out.println("hello");
System.out.println("Now calling the Dao factory to get hte customer dao object");
cao = DaoFactory.getCustomerDao();
cao.saveCustomer(custID,fname,lname,create_Date,is_deleted);
cao = null;
saveCustomerLog(custID,fname,lname);
}


public void saveCustomerLog(String custID, String fname, String lname){

System.out.println("hello");
System.out.println("Now calling the Dao factory to get hte customer dao object");
cao = DaoFactory.getCustomerDao();
cao.saveCustomerLog(custID,fname,lname);
cao = null;
}


This is my Dao implementation.
public void saveCustomer(String custID,String fname,String lname,String create_Date,String is_deleted){
openConnection();
System.out.println("came in the save customer of customer Dao implementation");
Statement stmt=null;
try{
stmt = conn.createStatement();
String strsql = "insert into customer values('"+ custID + "','" + fname + "','" + lname + "','" + create_Date + "','" + is_deleted + "')";
System.out.println(strsql);
stmt.executeQuery(strsql);
}catch(SQLException e){
e.printStackTrace();
}finally{
stmt = null;
closeConnection();
}

}

public void saveCustomerLog(String custID,String fname,String lname){
openConnection();
System.out.println("came in the save customer of customer Dao implementation");
Statement stmt=null;
try{
stmt = conn.createStatement();
String strsql = "insert into customer_log values('"+ custID + "','" + fname + "','" + lname + "')";
System.out.println(strsql);
stmt.executeQuery(strsql);
}catch(SQLException e){
e.printStackTrace();
}finally{
stmt = null;
closeConnection();
}

}



public void openConnection(){
System.out.println("came in the open connection...");
try{
Hashtable default_properties = new Hashtable();
Context context = new InitialContext();
DataSource ds = (DataSource) context.lookup("PORSCHETHIN");
conn= ds.getConnection();
}catch(Exception e){
System.out.println("error in getting connection");
e.printStackTrace();
}
}


And this is the ejb jar mehtod definition
<assembly-descriptor>
<container-transaction>
<method>
<ejb-name>CustomerTrans</ejb-name>
<method-name>saveCustomer</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>

<container-transaction>
<method>
<ejb-name>CustomerTrans</ejb-name>
<method-name>saveCustomerLog</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>

</assembly-descriptor>
Murali Pen
Greenhorn

Joined: May 23, 2006
Posts: 28
Ok, I have tested this in Websphere and everything I previously said was true. Here is the gist.

If you want the transaction to be rolled back when any of the EJB methods doing the DB inserts fail, you can do either of the following.

1) Invoke the EJB methods from the client's transaction context. In both your EJB methods, throw an EJBException from all the exception catch blocks. (Alternatively you can also mark the transaction for rollback by calling context.setRollBackOnly method).

2) Client has no transaction context and invokes EJB methods. Call m2 at the end of m1. In both your EJB methods, throw an EJBException from all the exception catch blocks. (Alternatively you can also mark the transaction for rollback by calling context.setRollBackOnly method).

In option 1, if you don't invoke m1 and m2 from the client's transaction context, container starts a new separate transaction scope for each method and ends when each method finishes. Hence, if one of them fails, the other won't be rolled back.

In option 2, container starts a transaction scope for m1 and propagates it to m2, since m2 is invoked from m1

Remember, everything I said above is valid only if the transaction attribute for both m1 and m2 is set as - required.

I have merely elaborated on what I said in my previous post. But I verified my claims by testing the scenario in websphere.

If it is still not clear to you, you can read about transactions in the EJB specification. I can post my code if you want to take a look. Also you mentioned that you tried invoking m2 from m1 but you did not tell me whether you got the correct behaviour.
[ June 14, 2006: Message edited by: Murali Pen ]
Pradeep bhatt
Ranch Hand

Joined: Feb 27, 2002
Posts: 8903

Rashid,
Why do you need XA compliant JDBC driver ? Are you accessing different databases?


Groovy
 
I agree. Here's the link: http://aspose.com/file-tools
 
subject: Problem with ejb transaction
 
Similar Threads
How to programmatically find if EJB is operating in transaction mode
container-transaction Tag - Who is Responsible?
Transaction handling with Stateless SessionBean
auto commit within JTS transaction
Weblogic 8.1 and ejb 1.0