Manual Rollback of transactions in Seam - java

This is a similar problem to Forcing a transaction to rollback on validation error
The scenario is this:
A user edits a page, the transaction is set to MANUAL so only if we call flush it would be committed to the database. Now the user wants to cancel the changes. Easy as you haven't flushed it yet.
Now consider this scenario: User edits page with lots of ajax on there. Some of these ajax callbacks require database queries (e.g. using a richFaces suggestion box etc). Some validation is done as well that requires database lookups. The problem is Hibernate will automatically issue a flush when you do a query. So the user doesn't press the save button (which would flush the transaction) he presses the cancel button. What do you do now?
If you don't do anything the changes will be written to the database - not what the user expects.
You can throw an exception that is annotated with
#ApplicationException(rollback=true)
That would rollback the transaction. You can then redirect to another page. However here I've come across another problem, on some pages you redirect to you get a lazy initialisation exception. I've specified
<exception class="com.mycomp.BookingCancelException">
<end-conversation before-redirect="true"/>
<redirect view-id="/secure/Bookings.xhtml">
<message severity="INFO">#{messages['cancel.rollback']}</message>
</redirect>
</exception>
in pages.xml, so the conversation should end before we are doing the redirect. A new conversation should start (with a new transaction) but that doesn't seem to happen in all cases? Why?
I've read somewhere else that you can simply use
Transaction.instance().rollback();
This would be preferable as you don't have to go via exceptions (the redirect always takes long when Seam handles exceptions) but the problem is that the Transaction isn't actually rolled back. I couldn't figure out why. If I check the status of the transaction it says that it is not in rollback state.
How would you best handle Cancel requests. The pure MANUAL flush doesn't work in this case. You could work with detached entities but the page contains several linked entities so this is getting messy.
Update: I've now discovered that throwing the ApplicationException doesn't rollback the transaction in all cases. So rather confused now.
Update 2: Of course rolling back transactions will not work when you have an page where you use ajax to update values. Each transaction only covers one request. So if you do e.g. 5 edits with ajax request, rolling back a transaction will only roll back the changes from the last ajax request and not from the earlier 4 ones.
So solution is really to use the flush mode MANUAL.
There are a few things that will cause a flush even if you specify MANUAL.
a query in an ajax request can trigger a flush - Use setFlushMode(FlushMode.COMMIT) on the query to avoid this.
Persisting an entity can trigger a flush depending on the ID generation used (e.g. if you use the strategy IDENTITY). You can work around this by using Cascades. If you need to create entities during the edit which don't have any real relationship with the main entity your are editing just add them to a list and persist all entities in that list when you do a save.
When you start a nested conversation or another bean joins the conversation the Flush Mode on that session is set back to AUTO when you don't specify #Begin(join=true,flushMode=FlushModeType.MANUAL)
You might want to specify MANUAL as the default mode in components.xml
<core:manager concurrent-request-timeout="10000"
conversation-id-parameter="cid" conversation-timeout="600000" default-flush-mode="MANUAL"/>

Have you tried
#Begin(flushMode=MANUAL)
someRandomValidationMethodHere(){ ... }
or setting
<core:manager conversation-timeout="120000" default-flush-mode="manual" />
in components.xml?

It might be helpful to know that you can tell the ajax validation components not to flush, using the attribute bypassUpdates. Here's an article explaining it.

You can manage your transaction and manage it with the button activated.
protected void doGet(HttpServletRequest request,
HttpServletResponse response, String butonPressed)
throws ServletException, IOException {
try {
// Begin unit of work
HibernateUtil.getSessionFactory()
.getCurrentSession().beginTransaction();
} catch (Exception ex) {
HibernateUtil.getSessionFactory()
.getCurrentSession().getTransaction().rollback();
throw new ServletException(ex);
}
finally
{
if ("SAVE".equals(butonPressed))
{
// Process request and render page...
// End unit of work
HibernateUtil.getSessionFactory()
.getCurrentSession().getTransaction().commit();
}
else
{
HibernateUtil.getSessionFactory()
.getCurrentSession().getTransaction().rollback();
}
}
}

Related

Does Session.getCurrentSession() closes without doing any transaction?

I am trying to load entity by doing this:
public void reloadRepository() {
Session session = getSessionFactory().getCurrentSession();
session.beginTransaction();
Hibernate.initialize(Repository.class);
}
From this stack overflow post (Hibernate openSession() vs getCurrentSession()), it says
When you call SessionFactory.getCurrentSession, it creates a new
Session if it does not exist, otherwise use same session which is in
current hibernate context. It automatically flushes and closes session
when transaction ends, so you do not need to do it externally.
What does it mean by "transaction ends"? If I don't make any transaction (guessing Hibernate.initialize() is not making transaction), does hibernate close this session?
Probably.
I'm guessing you set current_session_context_class to thread (since you're using beginTransaction). This means that, according to the javadoc, the session is only usable after transaction is started and is destroyed automatically when transaction ends.
I'm not sure what you mean by 'not making any transaction', you just made one using beginTransaction(). Once you commit or rollback, the transaction will end. Even if you do neither, the transaction will eventually time out,and that will also count as ending the transaction.
It's written like that because in modern apps you control transactions with the #Transactional annotation. You simply put it on top of the service methods and Hibernate opens a transaction automatically and closes it when it reaches the end of the method.
I don't really know what you think your last row of code is doing but it looks very wrong. If you want to load an entity you can simply write session.get(), add #Transactional to your method and delete session.beginTransaction() and Hibernate.initialize().

How to handle data inconsistency

My applications is on Spring MVC 4.2 with postgres database. In my application, we are consuming an API which is written using spring boot having its own database (mysql).
#Transaction(rollbackfor = Exception.class)
updateOrder(Order order) {
// This insert is part of my application
update(order); //**STEP - 1**
//This is not part of our application &
// happening in api written in spring boot.
Integer transactionId = updateOrderWorkflow(order);// **STEP - 2**
//Below updateOrderWithTransactionId is part of my application
//Updates the order with the transaction Id
updateOrderWithTransactionId(order, transactionId); //**STEP - 3**
}
If STEP-3 fails, then I have to rollback the changes made in the consuming api. For rolling back I have written compensation/rollback method, which rolls back to the old workflow status.
Now the problem scenario:
If one process (PROCESS_1) is working on the above updateOrder() method and reaches to STEP-3, but before this process FAILS in STEP-3, another process (PROCESS_2) tries to access the updateOrder() method and updates STEP-2. Now PROCESS_1 fails in STEP-3 and it calls compensation/rollback method, but PROCESS_2 completes STEP-3 successfully.
This creates data inconsistency. How to handle this situation?
It sounds like the problem is that updateOrderWorkflow in step 2 exposes the changes made by the transaction of PROCESS_1 before it has been committed.
What I would do is:
Change updateOrderWorkflow in step 2 so that it doesn't show uncommitted changes. Any changes it makes have to be made in a temporary space of some kind, associated with the transaction ID.
Add an API endpoint to this same API, to commit or roll back the changes of a transaction. If committed, the changes in the temporary space are made visible globally. If rolled back, the changes are discarded.
Use the new commit API endpoint in your updateOrder method, and the rollback API in your rollback handler.

Spring #Transaction (readonly=true) context commit data to DB in the middle of the process

I am working on an assignment to make the code transactional. I am having this problem about read-only transaction for while and none of a single suggestion I found on the internet didn't work. (Spring and hibernate integrated project)
This is my read-only transactional method
#Transactional(propagation=Propagation.REQUIRES_NEW, readOnly=true
,rollbackFor=Exception.class)
public void
editInternationalExportConsigment(InternationalExportConsignmentFormWrapper
exportConssi (){}
Inside this method, there is a translator process happening. Where the process fetch (select ) data from DB and set to an Object
Contact contact =inquiry.loadCustomerContactById(consignmentVO.getCustomerContactId().intValue());
if (contact != null && contact.getCity() != null) {
consignment.setOrgin(contact.getCity().getCountry());
consignment.setUniqueOriginCountry((contact.getCity().getCountry()!=null)?contact.getCity().getCountry().getId():null);
consignment.setOrginCity(contact.getCity());
}
There are no any update or insert query run in the middle, Only select. But end of the this code snippet execution it commit the data to DB (whatever the value set to setter method will persist into DB )
Can someone please tell me what wrong is happening here. your feedback will be much appricated.
After tedious research, I have found the answer. In our project there are two session factories are running. And also it uses spring OpenSessionInViewFilter to avoid 'lazy initializing' issue. OpenSessionInViewFilter has set flushMode to Auto. Since the OpenSessionInViewFilter keeping binding a hibernate session to the thread along in the entire process, it will override the transactional hibernate session object which gets creates once I start the new transaction.Therefore even if I kept the flushmode for transactional scope as 'COMMIT' it get override with the AUTO property by the properties declared OpenSessionInViewFilter.
When the flushMode is AUTO hibernate will flush dirty objects to DB.
Read this for understand hibernate data flushin
As a solution, I manually change the flush mode to 'COMMIT' inside my transactional method.
Thank you for everyone replied and commented. :)

How do I do nested transactions in hibernate using only one connection?

Context of problem I want to solve: I have a java spring http interceptor AuditHttpCommunicationInterceptor that audits communication with an external system. The HttpClieant that does the communication is used in a java service class that does some business logic called DoBusinessLogicSevice.
The DoBusinessLogicSevice opens a new transaction and using couple of collaborators does loads of stuff.
Problem to solove: Regardless of the outcome of any of the operations in DoBusinessLogicSevice (unexpected Exceptions, etc) I want audits to be stored in the database by AuditHttpCommunicationInterceptor.
Solution I used: The AuditHttpCommunicationInterceptor will open a new transaction this way:
TransactionDefinition transactionDefinition = new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
new TransactionTemplate(platformTransactionManager, transactionDefinition).execute(new TransactionCallbackWithoutResult() {
#Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
// do stuff
}
});
Everything works fine. When a part of DoBusinessLogicSevice throws unexpected exception its transaction is rolled back, but the AuditHttpCommunicationInterceptor manages to store the audit in the database.
Problem that arises from this solution: AuditHttpCommunicationInterceptor uses a new db connection. So for every DoBusinessLogicSevice call I need 2 db connections.
Basicly, I want to know the solution to the problem: how to make TransactionTemplate "suspend" the current transaction and reuse the connection for a new one in this case.
Any ideas? :)
P.S.
One idea might be to take a different design approach: drop the interceptor and create an AuditingHttpClient that is used in DoBusinessLogicSevice directly (not invoked by spring) but I cannot do that because I cannot access all http fields in there.
Spring supports nested transactions (propagation="NESTED"), but this really depends on the database platform, and I don't believe every database platform is capable of handling nested transactions.
I really don't see what's a big deal with taking connection from a pool, doing a quick audit transaction and returning connection back.
Update: While Spring supports nested transactions, it looks like Hibernate doesn't. If that's the case, I say: go with another connection for audit.

Handling an expired ViewState in JSF and Richfaces

I have a series signup pages that build on each other. When the users session expires I have a Listener that cleans everything up on the server and that works great. But, if the user attempts to do anything else I just want to redirect them back to the first page of the series. However, my filter doesn't seem to work correctly. I keep getting javax.faces.application.ViewExpiredException
What is the best practice for handling this expcetion? I can't really just handle in web.xml because that's too global. Plus the Error page is being rendered from some JSF code - it seems like I need to catch that this is happening using a PhaseListener so the exception doesn't happen in the first place, but I haven't been able to find a good model for how to do this. Any ideas?
Richfaces has their own mechanism for handling ViewExpiredException, look at Richfaces docs.
I think you are the correct track with a phase listener. Essentially set something up in session on the first page. Then in phase listener look for the value in session. If it doesn't exit then do a redirect. The trick is to do it early in the phase listener process.
Not sure exactly where in the process your phase listener is throwing the exception.
The way I handle this is to add a filter to the web.xml only mapped to the urls you want to track. That filter checks if the session is expired, and then forwards to a login page if it is. It should work if the filter is run before any JSF code is run.
The way I have handled this in the past is to use a listener which checks a field in a managed session bean. If the users session times out a listener cleans up the mbean and marks the user as not logged in. Every request is sent through the listener and if the requirements are not met then the user is forced out of the site. I never get ViewExpiredException in my log. The only time this exception has occurred is if the server has been restarted and the user requests a page when having a previous active session.
You can check whether you session is invalidate or not
boolean sessionInValid = httpServletRequest.getRequestedSessionId()
!= null &&
!httpServletRequest.isRequestedSessionIdValid();
Here the boolean variable sessionInValid will return true if the session is invalidate by any means.
You can add this in a filter or a listener then configure this in your web.xml file.

Categories

Resources