UnitOfWork and Transactions

Posts   
 
    
omar avatar
omar
User
Posts: 569
Joined: 15-Oct-2004
# Posted on: 01-Dec-2005 09:09:44   

I am porting some of my code from DataAccessAdapter to UOW and want to verify the transactional behaviour of UOW:

1- UOW.Commit(adapter, False) will not commit the uow elements. Is that the same as not calling uow.commit at all. example:

If myFlag=True Then
   uow.Commit(adapter,True)
End if

is the same as

   uow.Commit(adapter, myFlag)  

2- UOW.Commit(adapter, True) will commit the uow elements BUT if any element fails then all uow elemnts are rolledback * Is an exception the only cause a uow elemnt would fail? If NO then would a UOW object raise an exception if it fails?

3- If a UOW.Commit raises an exception, my exception handler does NOT need to rollback the UOW object because it does that for us?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39618
Joined: 17-Aug-2003
# Posted on: 01-Dec-2005 10:18:14   

omar wrote:

I am porting some of my code from DataAccessAdapter to UOW and want to verify the transactional behaviour of UOW:

1- UOW.Commit(adapter, False) will not commit the uow elements. Is that the same as not calling uow.commit at all. example:

If myFlag=True Then
   uow.Commit(adapter,True)
End if

is the same as

   uow.Commit(adapter, myFlag)  

No. Commit performs the actions you've been storing inside the UoW. Not calling Commit therefore doesn't do anything. Calling Commit without telling it to autocommit the transaction does perform the actions but the transaction isn't committed yet (only the UnitOfWork) and therefore you can perform more actions inside the same transaction.

2- UOW.Commit(adapter, True) will commit the uow elements BUT if any element fails then all uow elemnts are rolledback

It will perform all actions in the UoW. If something fails, it will rollback its current transaction, which is the transaction the adapter is in, IF autocommit is true. So if you've passed adapter to other UoW's and not specified 'true' with Commit, those actions are rolled back as well.

You can use savepoints in between the UoW calls. :


Dim adapter As New DataAccessAdapter()
adapter.StartTransaction(IsolationLevel.ReadCommitted, "SaveUoWs")
uow1.Commit(adapter, False)     ' don't commit the transaction yet
' save the transaction at this point.
adapter.SaveTransaction("uow1");

'...

uow2.Commit(adapter, False)     ' don't commit the tranasaction yet
' save the transaction at this point.
adapter.SaveTransaction("uow2");

'...

uowN.Commit(adapter, True)      ' commit all

THis is of course a bogus piece of code but it illustrates what's going on. If in between something is going wrong, you can roll back to a savepoint, like uow1 and skip uow2 for example, but still keep the actions performed by uow1.

  • Is an exception the only cause a uow elemnt would fail? If NO then would a UOW object raise an exception if it fails?

UoW's just execute actions like you would do that too in your code. So if something is seriously wrong, like a query crashes inside the DB, an exception is received and the whole transaction is aborted, IF autoCommit is set to True. If autocommit was set to false, the transaction isn't rolled back, because the caller apparently wanted to do more with the transaction and has commit code in place, which comes with rollback code as well, normally, so it then won't roll back the transaction. In all cases the exception is rethrown. If a save fails but no exception is thrown, the UoW isn't aborting the transaction. This is the case if validation code reports a failure but doesn't throw an exception, for example if validation isn't critical for a transaction but is critical for that particular entity.

3- If a UOW.Commit raises an exception, my exception handler does NOT need to rollback the UOW object because it does that for us?

If you specify true for autoCommit then yes. If you specify false for autocommit, you have an adapter.Commit() somewhere in your own code anyway, which also has a catch and an adapter.Rollback.

Frans Bouma | Lead developer LLBLGen Pro
omar avatar
omar
User
Posts: 569
Joined: 15-Oct-2004
# Posted on: 02-Dec-2005 11:10:23   

one more question: 1- create an adapter object (adapter1) and start a transaction 2- UOW1 collects actions and uses adapter to commit (no auto-commit) 3- UOW2 collects actions and uses adapter to commit (no auto-commit) 4- UOW3 collects actions and uses adapter to commit (no auto-commit)

5- adapter1.Commit

Question (1) will commiting adapter1 object result in committing all UOW objects that use it (you said that rolling-back adapter1 would roll-back all UOW objects)? If yes, then can I assume that comitting adapter1 would implicitly call the Commit method of each UOW object participating in adapter1's transaction?

Question (2) if UOW3 fails for some reason, this WILL rollback all actions inside the transaction from all UOW objects and other non-UOW actions that are using the same adapter?

Question (3) if UOW3 did an autoCommit, what will happen after the point of succesful autoCommit if: * I commit adapter1 * I do actions with adapter1 then try to rollback/commit adapter1 * I add another UOW object that uses the same adapter and try to commit it * I add more actions to UOW3 itself and try to commit it?

I know I started by saying ONE question... but I am trying to finalize this design-pattern for JCL1.5...

omar avatar
omar
User
Posts: 569
Joined: 15-Oct-2004
# Posted on: 02-Dec-2005 15:28:20   

I wanted to put this question in a seperate message. Question is best explained by the following pseudo code:

fn1()

create uow1 uow1.addAction1 uow1.addAction2 call fn2(uow1) uow1.commit(auto:=True) end

fn2(uowParam)

uowParam.addAction3 uowParam.addAction4 uowParam.Commit(auto:=False) <--- is this necessary end

The question is about the marked line in fn2(). Is there a difference between having this line and NOT having it at all?

Walaa avatar
Walaa
Support Team
Posts: 14951
Joined: 21-Aug-2005
# Posted on: 02-Dec-2005 16:26:58   

one more question: 1- create an adapter object (adapter1) and start a transaction 2- UOW1 collects actions and uses adapter to commit (no auto-commit) 3- UOW2 collects actions and uses adapter to commit (no auto-commit) 4- UOW3 collects actions and uses adapter to commit (no auto-commit) 5- adapter1.Commit

This contradicts with what UnitOfWork is used for, please read the following from the LLBLGen Pro documentation: Sometimes actions on entities span a longer timeframe and / or multiple screens. It's then often impossible to start a database transaction as user-interaction during a transaction should be avoided. To track all the changes made and to persist them in one transaction can then be a tedious task. With the UnitOfWork2 class this can be solved.

Walaa avatar
Walaa
Support Team
Posts: 14951
Joined: 21-Aug-2005
# Posted on: 02-Dec-2005 16:35:13   

The following is also copied from the LLBLGen Pro documentation:

_The actions are not executed until Commit() is called. Commit() always expects a DataAccessAdapter object which is used to run the persistent actions. You don't have to start a transaction yourself, if the passed in DataAccessAdapter is not controlling a transaction yet, a new one is started. You can commit more than one UnitOfWork2 object in one transaction, simply pass the same DataAccessAdapter object to all Commit() calls, passing false for autoCommit. Commit() can also autocommit the transaction, if all the actions succeed, you then have to use the overload of Commit() which expects a boolean, autoCommit. _

Walaa avatar
Walaa
Support Team
Posts: 14951
Joined: 21-Aug-2005
# Posted on: 02-Dec-2005 16:56:15   

To give you a good view of what's happening inside the UOW.Commit()

public virtual void Commit(ITransaction transactionToUse,   bool autoCommit)
{
    ... //some code

    if(!adapter.IsTransactionInProcess)
        adapter.StartTransaction();

    try
    {
        ... // code that uses adapter to Insert/Update/Delete...

        if(autoCommit ==true)
            adapter.Commit();
    }
    catch
   {
        if(autoCommit ==true)
            adapter.Rollback();
   }

}

omar avatar
omar
User
Posts: 569
Joined: 15-Oct-2004
# Posted on: 02-Dec-2005 17:14:31   

Thanks Walaa.. maybe I should've refered to the documentation before running to the forum.

One last question.. If one or more UOW objects are using the same adapter object and I choose to commit the adapter object myself using adapter.commit.

1- This would commit all actions in all UOW objects ?

2- If answer of 1 is yes, does committing the UOW objects meen comitting their actions or calling each UOW object's commit method?

Marcus avatar
Marcus
User
Posts: 747
Joined: 23-Apr-2004
# Posted on: 03-Dec-2005 16:18:29   

omar wrote:

Thanks Walaa.. maybe I should've refered to the documentation before running to the forum.

One last question.. If one or more UOW objects are using the same adapter object and I choose to commit the adapter object myself using adapter.commit.

1- This would commit all actions in all UOW objects ?

2- If answer of 1 is yes, does committing the UOW objects meen comitting their actions or calling each UOW object's commit method?

Remember that the "Commit" has nothing to do with UnitOfWork or the DataAccessAdapter, you are committing the "Transaction". The question is then who started the transaction and by that I mean who owns the transaction? smile

I always ensure that only the method which starts a transaction should be responsibility for committing or rolling it back. Keeping this in mind makes the process simply. I think the inclusion of the autoCommit flag in uow.Commit() causes people a lot of consusion and (sorry Frans) I don't think it should have been included in the API. stuck_out_tongue_winking_eye I think it was put there for convenience but it only serves to allow an object other than the transaction's owner to commit or roll it back. Doing this is bad practice in my opinion. simple_smile

So if there is no transaction in progress you call uow.Commit(adapter, true) otherwise you call uow.Commit(adapter, false). To simply this further I always call:

uow.Commit(adapter, !adapter.IsTransactionInProgress);

Hope this helps.