Details
-
Improvement
-
Status: Closed
-
Major
-
Resolution: Fixed
-
None
-
None
-
Any
Description
At times, a method call may need to call another method call as part of one logical data insert/update operation. Currently the problem is that iBatis does not expose in the session interface whether or not their is an open transaction. Because of this, a method that may be called as part of a larger data update has no way of checking to see if a transaction is open before opening another one. Some RDMSs allow nested transactions and this is not a problem, but other providers do not and it can become a problem. The suggestion is that iBatis expose the _isOpenTransaction field on the session via a property so that it can be checked in a method call when a method needs to determine if it should open a transaction or not.
Below is a sample use case where ObjectB can be updated, but ObjectB is also a property of ObjectA and when ObjectA is updated, the embedded ObjectB should be updated as well all within one transaction. (This is just an example, not great code).
class Example
{
private ISqlMapper _mapper = null;
public Example()
{ _mapper = Mapper.Instance(); }public void UpdateA(ObjectA a)
{ _dataMapper.StartTransaction(); _dataMapper.update("update-a", a); UpdateB(a.ObjectB); _dataMapper.CommitTransaction(); } public void UpdateB(ObjectB b)
{
bool existingTransaction = _dataMapper.TransactionOpen;
if (!existingTransaction)
_dataMapper.Update("update-b", b);
if (!existingTransaction)
}
}
This has been a major need for us on some projects and I would suppose for others as well. For that reason, we are proposing this enhancement rather than creating some in house solution or a custom iBatis build. In order to make the changes, the following changes would need to be made to the code:
The IBatisNet.Common.IDalSession interface would need the following new property:
bool OpenTransaction
{ get; }The IBatisNet.DataMapper.SqlMapSession would need the following changes:
1. Implement the new property to expose the existing field.:
public bool OpenTransaction
{
get
}
2. Update the CommitTransaction methods to be as follows including an update to the _isOpenTransaction field:
/// <summary>
/// Commits the database transaction.
/// </summary>
/// <remarks>
/// Will close the connection.
/// </remarks>
public void CommitTransaction()
{
if (_logger.IsDebugEnabled)
_transaction.Commit();
_transaction.Dispose();
_isOpenTransaction = false;
if (_connection.State != ConnectionState.Closed)
}
/// <summary>
/// Commits the database transaction.
/// </summary>
/// <param name="closeConnection">Close the connection</param>
public void CommitTransaction(bool closeConnection)
{
if (closeConnection)
{ this.CommitTransaction(); }
else
{
_transaction.Commit();
if (_logger.IsDebugEnabled)
{ _logger.Debug("Commit Transaction."); }
_transaction.Dispose();
_isOpenTransaction = false;
}
}
3. Update the RollbackTransaction methods to set the state of the _isOpenTransaction field as follows:
/// <summary>
/// Rolls back a transaction from a pending state.
/// </summary>
/// <remarks>
/// Will close the connection.
/// </remarks>
public void RollBackTransaction()
{
_transaction.Rollback();
if (_logger.IsDebugEnabled)
{ _logger.Debug("RollBack Transaction."); }
_transaction.Dispose();
_transaction = null;
_isOpenTransaction = false;
if (_connection.State != ConnectionState.Closed)
{ this.CloseConnection(); }
}
/// <summary>
/// Rolls back a transaction from a pending state.
/// </summary>
/// <param name="closeConnection">Close the connection</param>
public void RollBackTransaction(bool closeConnection)
{
if (closeConnection)
else
{
if (_logger.IsDebugEnabled)
_transaction.Rollback();
_transaction.Dispose();
_transaction = null;
_isOpenTransaction = false;
}
}
This would be very helpful when developing against RDMS servers and/or providers that do not support nested transactions as well as be more efficient than nested transactions.