-
1. Re: flushMode= MANUAL, when does commit happen? And other qu
gavin.king Jul 12, 2007 10:34 AM (in response to ellenzhao)Transaction commit is different to flushing the PC. Tx commits happen on every request. Flush occurs when you explicitly call entityManager.flush() or session.flush().
Don't mess with hibernate.connection.autocommit, that is something completely unrelated. -
2. Re: flushMode= MANUAL, when does commit happen? And other qu
ellenzhao Jul 12, 2007 10:41 AM (in response to ellenzhao)Okay, thanks!
How can the persisted motherEntity be removed when the root conversation is time out? -
3. Re: flushMode= MANUAL, when does commit happen? And other qu
delphi'sghost Jul 12, 2007 11:10 AM (in response to ellenzhao)AFAIK, you cannot perform multiple data entry conversations in a nested fashion without having an all-or-nothing problem with regards to saving the data.
The problem is that each nested conversation uses the same persistence context, and when you flush for a nested conversation, you are flushing for all conversations using that persistence context (which includes all parent and child conversations).
The conclusion I reached was that unfortunately, the nested conversation model was only good for browsing/navigating data due to the fact that it shares the persistence context. The Seam Issues example which was the only example to have editable pages in nested conversations was removed from the latest release (hope they re-write it to show how it should be done).
Here are a couple of threads related to persistence contexts and nested conversations:
http://www.jboss.com/index.html?module=bb&op=viewtopic&t=111681
http://www.jboss.com/index.html?module=bb&op=viewtopic&t=111384
However, thinking about your problem more, you may be able to do something like :
Start root conversation (with flushmode = automatic)
Create mother entity
Persist mother entity
Set the entityManager flush mode to Manual
At this point, you are in a manual flush mode, with the mother entity already created and persisted. If the child item also has child items, then you are in a sticky problem. Also this solution doesn't handle the problem of :
Edit Mother Entity
Add child
Edit child
Flush entity manager to save child -> Ooops, we just accidentally saved the changes to the mother also since the child is edited in a nested conversation, and we flushed the shared persistence context.
Also, if your 'add child' or 'edit child' links could be opened in new windows, then you could have accidental flushes in each conversation when you just meant to save and flush one instance of the child editor.
As for rolling back the mother entity in the event of a conversation timeout...
If you have a flag called isNew which indicates whether it is a new entity or not, you can write a cancel changes procedure, something like:String cancelChanges() { if (isNew) { em.remove(motherItem); } else { em.refresh(motherItem); } }
I don't know the feasability of this, perhaps someone with more knowledge than me can comment, but you have a method marked with the @Destroy annotation, you could just call the cancel changes method from there if it has not already been saved (i.e. isNew is still true).
This might be icky, but come to think of it, I'm wondering if this is a pretty handy pattern because what the conversation times out and you have made edits to the entity involved? Those edits could sit around in the persistence context after the conversation has timed out. Should the entity be refreshed because the persistence context might be re-used with the dirty object in it? -
4. Re: flushMode= MANUAL, when does commit happen? And other qu
ellenzhao Jul 12, 2007 12:06 PM (in response to ellenzhao)Hi DG!
Thank you very much for your informative input! In my original post, I was dealing with the creation of a new motherEntity (which is a complex process...). So the isNew flag would always be true....However the flag idea is good. I can set a completedOrCanceled flag. Initialize it as false. The @End methods can be like this:@End public void cancelMotherCreation(){ // at this point, the children collections may or may not have been filled, // but it does not matter, since we can cascade.... entityManager.remove(motherEntity); this.completedOrCanceled = true; } @End public void newMotherDone(){ entityManager.merge(motherEntity); this.completedOrCanceled = true; }
Here the @Destroy method:@Destroy@Remove public void destroy(){ if (this.completedOrCanceled == false) entityManager.remove(motherEntity); }
I've got to try it out right now....
Regards,
Ellen -
5. Re: flushMode= MANUAL, when does commit happen? And other qu
ellenzhao Jul 12, 2007 12:14 PM (in response to ellenzhao)Oh by the way, I don't think this is icky at all...yes, a pretty handy pattern. My new-motherEntity.xhtml page has 5 or 6 parts are rounded with
<s:fragment rendered = .....> ... </s:fragment>
And several of the fragment is modal (which is a nested conversation). The mother Entity itself it too simple to be displayed on a separate page and I want my users to keep a big picture in mind when she is creating this motherEntity(which has 5 or 6 collections). So, everything happens on the same page when the user is creating a new motherEntity.
It takes a bit work to get straight of issues and I have had long debugging sessions, but now the result turns out pretty good. Seems the rolling-back-at-time-out problem is the last issue I need to solve for this root conversation.
Regards,
Ellen -
6. Re: flushMode= MANUAL, when does commit happen? And other qu
delphi'sghost Jul 12, 2007 3:33 PM (in response to ellenzhao)Origingally, my icky comment was related to the fact that I think you *might* run into a problem using the rollback code on a method annotated with @Remove and @Destroy since I don't know whether these annotations result in the method being called at other times (i.e. the bean is passivated, and not destroyed).
However, having a isCompleteOrCancelled flag would guard against any other calls you might get to that function. -
7. Re: flushMode= MANUAL, when does commit happen? And other qu
ellenzhao Jul 12, 2007 5:32 PM (in response to ellenzhao)yes tested, it works....The @Destroy method is now like this....
@Remove @Destroy public void destroy() { if (!this.completedOrCanceled) { if (this.motherEntity.getId() != null) { entityManager.merge(this.recipe); // without merging there might be exception saying you are trying to remove a detached object..... entityManager.remove(this.recipe); entityManager.flush(); } } }
-
8. Re: flushMode= MANUAL, when does commit happen? And other qu
christian.bauer Jul 12, 2007 6:38 PM (in response to ellenzhao)This is very suspicious code, it should not work and this should be correct:
entityManager.remove( entityManager.merge(this.recipe) );
Merge does not reattach the given instance, it returns an "attached" managed instance. Which is the right state for removing. -
9. Re: flushMode= MANUAL, when does commit happen? And other qu
ellenzhao Jul 13, 2007 4:22 AM (in response to ellenzhao)Ah yes! Thanks Christian! There are more places I used JPA's merge as Hibernate's reattach, need to clean those up....
I thought about whether the entityManager.flush() at the end of the destroy() method is necessary. If this root conversation will be nested into yet another Überconversation later, then the flush makes sense. Otherwise it may well be unnecessary.
Regards,
Ellen -
10. Re: flushMode= MANUAL, when does commit happen? And other qu
ellenzhao Jul 13, 2007 1:19 PM (in response to ellenzhao)I just cleaned up all the unnecessary/incorrect merges in my application, now the memory footprint is noticeably smaller. Thank Christian again for correcting me.
Unfortunately my original problem is not solved yet. I cannot seem to do flush in the @Destroy@Remove method, thus the removal of the incomplete motherEntity cannot be reflected in the database, the data integrity is harmed if any user leaves the conversation without explicitly ending it (either complete or cancel), or the server has a reboot when a conversation is in process....this is a pretty critical problem.
I tried explicit deletion using EQL in the @Destroy@Remove method too, it did not help.
The concrete error messages I got were always something like "no transaction available", still seeking a solution....Any suggestion will be highly appreciated! -
11. Re: flushMode= MANUAL, when does commit happen? And other qu
ellenzhao Jul 13, 2007 4:52 PM (in response to ellenzhao)okay, added one more column to the table which represents the motherEntity. That column stores the status of the motherEntity. When it's created by the session bean, status is set to "creating". When user explicitly completed the creation, status is set to "created". Other status like "updating", "updated" can also be added and set correctly...
-
12. Re: flushMode= MANUAL, when does commit happen? And other qu
matt.drees Jul 14, 2007 2:17 AM (in response to ellenzhao)"ellenzhao" wrote:
The concrete error messages I got were always something like "no transaction available"
Well, you could always manually get a transaction with Transaction.instance() and begin/commit.
But probably the right way is marking the method as needing a transaction. If it's a javabean component, you could use @Transactional. I don't know much how Ejb works, but I imagine there's a way you can tell the container it needs a transaction, too. -
13. Re: flushMode= MANUAL, when does commit happen? And other qu
ellenzhao Jul 14, 2007 7:19 AM (in response to ellenzhao)Thanks for Matt's tip, I tried this:
@Destroy @Remove public void destroy() { UserTransaction tx = Transaction.instance(); try { tx.begin(); entityManager.remove(entityManager.merge(this.recipe)); try { tx.commit(); } catch (SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalStateException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (RollbackException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (HeuristicMixedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (HeuristicRollbackException e) { // TODO Auto-generated catch block e.printStackTrace(); } } catch (NotSupportedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SystemException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
And got a javax.transaction.NotSupportedException when trying to shut down the jboss during a mother conversation.
I wish Seam could provide something like @Begin(atomic = true). With this "atomic = true" flag on, if this conversation does not reach its own @end before time out or a application server crash, any insert/delete/update in the database could be rolled back, so that a long running conversation can map to an atomic database transaction and the correct semantic of an entity in the database can be guaranteed....Or if there is already something like this or there is some way I can achieve the atomic conversation behaviour, please let me know and I highly appreciate it!
For now I add a "status" flag in the database, my application is aware that only records with the status "created" or "updated" are semantically correct and usable. -
14. Re: flushMode= MANUAL, when does commit happen? And other qu
ellenzhao Jul 14, 2007 1:19 PM (in response to ellenzhao)Feature request filed:
http://jira.jboss.com/jira/browse/JBSEAM-1664
I guess it is not uncommon for developers to mentally map a long-running conversation to an atomic DB transaction. If you feel this feature is also handy to you, please vote it on JIRA, thanks!