In this configuration with container-managed transactions, the transaction boundary is the ejb method call. Struts action classes make calls to the session ejb methods in order to retrieve data for display in various jsps. The problem is that after the ejb method returns, the transaction is over, the persistence context has ended and the pojos returned are detached. If the jsp that displays the data tries to access any lazily loaded associations an exception occurs. One solution might be to load every association eagerly but this could become very expensive in terms of performance and is definitely not scalable. Another approach might be to write different ejb methods that initialize the associations needed in different views but this will make the API between the web and middle tier very complicated.
What I finally did was borrowed by Hibernate. Hibernate suggests the use of the HibenateUtil class that manages transactions and is called usually in an HttpFilter. In this configuration the transaction boundary is the http request. In my case, I extended the Struts RequestProcessor and overridden the
process
method.
public class MyRequestProcessor extends RequestProcessor {
private static Logger logger = Logger.getLogger(MyRequestProcessor.class);
private static final ThreadLocal<UserTransaction> threadTransaction = new ThreadLocal<UserTransaction>();
@Override
public void process(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
UserTransaction utx = null;
try {
utx = threadTransaction.get();
if (utx == null) {
Context ctx = new InitialContext();
UserTransaction utx = (UserTransaction) ctx.lookup("UserTransaction");
threadTransaction.set(utx);
utx.begin();
}
super.process(request, response);
utx.commit();
}
catch (Exception e){
try {
if (utx != null)
utx.rollback();
}
catch (Exception e1) {
logger.error("Cannot rollback transaction", e1);
}
throw new ServletException(e);
}
finally {
threadTransaction.remove();
}
}
}
At the beginning of the processing the UserTransaction is initialized and after the processing it is committed. If an exception happens it is rolled back. By using this approach we make sure that the transaction and the persistence context are active after the ejb method returns and during the action and jsp execution thus enabling us to traverse association from inside the jsps. This approach has a problem though. If a forward is done from a struts action to another the request processor is called again in the same thread and thus the transaction will be committed twice (once per request processor call) resulting in an exception in the second attempt to commit. This can be resolved by use of some flag that marks the outermost call, so that we do the commit only there.
Another approach would be the use of an http filter that begins and commits the transaction thus moving the transaction boundaries at the http request.
All the above approaches do not make any reference to Hibernate classes so the implementation is persistence provider agnostic.
4 comments:
Its a nice work around indeed to extend the transaction..though in my case I really like to work with the 2 action pattern (not official name- thats something from my mind). I always do the PrepareDoingSomethingAction and the forward to the DoingSomethingAction..especially in cases where i have to retrieve lots of data and makey manipulations regarding the web layer.
I have to admit..i am sticking with the a bit complex DAO API...I feel thatn extending the transaction to the web layer (if we can call the Struts Action strictly web layer) ..is a bit of a conflict for the 2 main layers web - business!
Anyway keep blogging..its very interesting knowing that someone out there..faces the same problems as you do...really!
"I have to admit..i am sticking with the a bit complex DAO API"
Consider that my data model consists of a Customer who has a set of Orders. Each Order has a set of Items and each item corresponds to a Document which in turn has other collections inside. I have jsps that only need to display the Customer 's name and jsps that need to display Document 's details for a particular Customer. Leaving eager loading outside (it would be a nightmare to load the whole tree) I would have to define a getCustomer that returns only the Customer, a getCustomerWithOrders, a getCustomerWithOrdersWithItems or a getOrdersForCustomer and so on. You get the idea. This is a complex API that if another developer tries to use he/she will definitely call the wrong method sometime.
".I feel thatn extending the transaction to the web layer (if we can call the Struts Action strictly web layer) ..is a bit of a conflict for the 2 main layers web - business!"
That was exactly my hesitation at first, but then my co-worker (and jhug member :-)) convinced me that it will not be a problem because the layer mixing is done implicitly by the container. The jsps deal only with pojos that are fed to them by the Actions. Actions talk only to the session ejb, and the session ejb talks only to the entity manager. So the code is clean. No layer mixing. The mixing happens when the jsp tries to access a lazy property that causes a query to be executed in the background, but this is done by the container. My jsp knows nothing. The only "dirty" point (and the actual cause of the mixing) is the UserTransaction begin and commit in the RequestProcessor, but I can live with that. Se zalisa :-)
"its very interesting knowing that someone out there..faces the same problems as you do...really!"
Indeed!
It could be argued that extending the transaction across the web layer constitutes a layering violation. However we must keep in mind that any layering architecture is not godsend, but the result of an engineering trade off (usually maintainability versus efficiency). If (and only if) your application has no real use for a separate extra layer between the web tier and the persistence tier (like, say another non-web client), then this particular layering becomes irrelevant.
Moreover, if you consider the transactional behavior of the application as a different aspect (which it is) from the business logic, then no layering violation can be observed. One more reason for using lightweight containers instead of Java EE ones :-)
ok I have to be fair here when it comes to layer violation! JBoss Seam ...extends the persistence layer to the web layer..and is promoted as a feature indeed..so ok maybe I am too strict on that one!
When it comes to DAO, probably i insist on this way because I used to write in almost all the projects I was involved. I feel quite comfortable enriching my DAO Api with custom methods, since every time i call them, I know what i really fetch from the DB. From the DB interaction point of view my way is the optimal..from maintenace or development time..I agree it might not be optimal.
Believe me at this time being I have to deal with a far more complex tree of objects..multiple layers of parents and childers - Lazy objects nightmare :(
Mhpws telika na to gyrisoume sto Seam na teleiwnoume me ola ta parapanw mias kai opws fainetai o Gavin King ta exei lysei ta provlhmata afta xe xe xe
Post a Comment