Author: rgrabowski
Date: Sun Jun 28 05:30:59 2009
New Revision: 789049
URL: http://svn.apache.org/viewvc?rev=789049&view=rev
Log:
Enabled calls to ExecuteQueryForDataTable to be cached.
Added CachingStatementExecute (inspired by MappedStatement.Execute) to raise
and set default CacheKey properties in a consistent manner.
Documented that some calls to CachingStatement (i.e. those taking delegate and
explicit result objects) are never cached.
Modified:
ibatis/branches/MappedStatementRefactor/Apache.Ibatis.DataMapper/MappedStatements/CachingStatement.cs
Modified:
ibatis/branches/MappedStatementRefactor/Apache.Ibatis.DataMapper/MappedStatements/CachingStatement.cs
URL:
http://svn.apache.org/viewvc/ibatis/branches/MappedStatementRefactor/Apache.Ibatis.DataMapper/MappedStatements/CachingStatement.cs?rev=789049&r1=789048&r2=789049&view=diff
==============================================================================
---
ibatis/branches/MappedStatementRefactor/Apache.Ibatis.DataMapper/MappedStatements/CachingStatement.cs
(original)
+++
ibatis/branches/MappedStatementRefactor/Apache.Ibatis.DataMapper/MappedStatements/CachingStatement.cs
Sun Jun 28 05:30:59 2009
@@ -48,7 +48,10 @@
[DebuggerDisplay("MappedStatement: {mappedStatement.Id}")]
public sealed class CachingStatement : MappedStatementEventSupport,
IMappedStatement
{
- private readonly MappedStatement mappedStatement =null;
+ // Func<T1, T2, T3, T4, T5, TResult>
+ delegate T RequestRunner<T>(RequestScope requestScope, ISession
session, object parameter, CacheKey cacheKey, out bool cacheHit);
+
+ private readonly MappedStatement mappedStatement;
/// <summary>
/// Event launch on Execute query
@@ -108,8 +111,21 @@
/// <returns>The object</returns>
public DataTable ExecuteQueryForDataTable(ISession session, object
parameterObject)
{
- // TODO: add cache support
- return mappedStatement.ExecuteQueryForDataTable(session,
parameterObject);
+ RequestRunner<DataTable> requestRunner = delegate(RequestScope
requestscope, ISession session2, object parameter, CacheKey cachekey, out bool
cachehit)
+ {
+ cachehit = true;
+ DataTable dataTable = Statement.CacheModel[cachekey] as
DataTable;
+ if (dataTable == null)
+ {
+ cachehit = false;
+ dataTable =
mappedStatement.RunQueryForDataTable(requestscope, session, parameter);
+ Statement.CacheModel[cachekey] = dataTable;
+ }
+
+ return dataTable;
+ };
+
+ return CachingStatementExecute(PreSelectEventKey,
PostSelectEventKey, session, parameterObject, "ExecuteQueryForDataTable",
requestRunner);
}
/// <summary>
@@ -125,34 +141,34 @@
///<exception
cref="Apache.Ibatis.DataMapper.Exceptions.DataMapperException">If a transaction
is not in progress, or the database throws an exception.</exception>
public IDictionary ExecuteQueryForMap(ISession session, object
parameterObject, string keyProperty, string valueProperty)
{
- IDictionary map = new Hashtable();
-
- RequestScope request =
Statement.Sql.GetRequestScope(this, parameterObject, session);
-
- mappedStatement.PreparedCommand.Create( request,
session, Statement, parameterObject );
-
- CacheKey cacheKey = GetCacheKey(request);
- cacheKey.Update("ExecuteQueryForMap");
- if (keyProperty!=null)
- {
- cacheKey.Update(keyProperty);
- }
- if (valueProperty!=null)
- {
- cacheKey.Update(valueProperty);
- }
+ // this doesn't need to be in its own RunQueryForCachedMap method
because the class is sealed and can't be called by anyone else
+ RequestRunner<IDictionary> requestRunner = delegate(RequestScope
requestscope, ISession session2, object parameter, CacheKey cachekey, out bool
cachehit)
+ {
+ if (keyProperty != null)
+ {
+ cachekey.Update(keyProperty);
+ }
+ if (valueProperty != null)
+ {
+ cachekey.Update(valueProperty);
+ }
+
+ cachehit = true;
+ IDictionary map = Statement.CacheModel[cachekey] as
IDictionary;
+ if (map == null)
+ {
+ cachehit = false;
+ map = mappedStatement.RunQueryForMap(requestscope,
session, parameter, keyProperty, valueProperty, null);
+ Statement.CacheModel[cachekey] = map;
+ }
- map = Statement.CacheModel[cacheKey] as IDictionary;
- if (map == null)
- {
- map = mappedStatement.RunQueryForMap( request,
session, parameterObject, keyProperty, valueProperty, null );
- Statement.CacheModel[cacheKey] = map;
- }
+ return map;
+ };
- return map;
+ return CachingStatementExecute(PreSelectEventKey,
PostSelectEventKey, session, parameterObject, "ExecuteQueryForMap",
requestRunner);
}
- #region ExecuteQueryForMap .NET 2.0
+ #region ExecuteQueryForMap .NET 2.0
/// <summary>
/// Executes the SQL and retuns all rows selected in a map that is
keyed on the property named
@@ -167,36 +183,40 @@
///<exception
cref="Apache.Ibatis.DataMapper.Exceptions.DataMapperException">If a transaction
is not in progress, or the database throws an exception.</exception>
public IDictionary<K, V> ExecuteQueryForDictionary<K, V>(ISession
session, object parameterObject, string keyProperty, string valueProperty)
{
- IDictionary<K, V> map = new Dictionary<K, V>();
- RequestScope request = Statement.Sql.GetRequestScope(this,
parameterObject, session);
-
- mappedStatement.PreparedCommand.Create(request, session,
Statement, parameterObject);
-
- CacheKey cacheKey = GetCacheKey(request);
- cacheKey.Update("ExecuteQueryForMap");
- if (keyProperty != null)
- {
- cacheKey.Update(keyProperty);
- }
- if (valueProperty != null)
- {
- cacheKey.Update(valueProperty);
- }
+ // this doesn't need to be in its own RunQueryForCachedDictionary
method because the class is sealed and can't be called by anyone else
+ RequestRunner<IDictionary<K, V>> requestRunner =
delegate(RequestScope requestScope, ISession session2, object parameter,
CacheKey cacheKey, out bool cacheHit)
+ {
+ if (keyProperty != null)
+ {
+ cacheKey.Update(keyProperty);
+ }
+ if (valueProperty != null)
+ {
+ cacheKey.Update(valueProperty);
+ }
+
+ cacheHit = true;
+ IDictionary<K, V> map = Statement.CacheModel[cacheKey] as
IDictionary<K, V>;
+ if (map == null)
+ {
+ cacheHit = false;
+ map = mappedStatement.RunQueryForDictionary<K,
V>(requestScope, session2, parameter, keyProperty, valueProperty, null);
+ Statement.CacheModel[cacheKey] = map;
+ }
- map = Statement.CacheModel[cacheKey] as IDictionary<K, V>;
- if (map == null)
- {
- map = mappedStatement.RunQueryForDictionary<K, V>(request,
session, parameterObject, keyProperty, valueProperty, null);
- Statement.CacheModel[cacheKey] = map;
- }
+ return map;
+ };
- return map;
+ return CachingStatementExecute(PreSelectEventKey,
PostSelectEventKey, session, parameterObject, "ExecuteQueryForDictionary",
requestRunner);
}
/// <summary>
/// Runs a query with a custom object that gets a chance
/// to deal with each row as it is processed.
/// </summary>
+ /// <remarks>
+ /// This method always bypasses the cache.
+ /// </remarks>
/// <param name="session">The session used to execute the
statement</param>
/// <param name="parameterObject">The object used to set the
parameters in the SQL. </param>
/// <param name="keyProperty">The property of the result object to be
used as the key. </param>
@@ -206,7 +226,7 @@
/// <exception
cref="Apache.Ibatis.DataMapper.Exceptions.DataMapperException">If a transaction
is not in progress, or the database throws an exception.</exception>
public IDictionary<K, V> ExecuteQueryForDictionary<K, V>(ISession
session, object parameterObject, string keyProperty, string valueProperty,
DictionaryRowDelegate<K, V> rowDelegate)
{
- return mappedStatement.ExecuteQueryForDictionary<K, V>(session,
parameterObject, keyProperty, valueProperty, rowDelegate);
+ return mappedStatement.ExecuteQueryForDictionary(session,
parameterObject, keyProperty, valueProperty, rowDelegate);
}
#endregion
@@ -214,6 +234,9 @@
/// Execute an update statement. Also used for delete statement.
/// Return the number of row effected.
/// </summary>
+ /// <remarks>
+ /// This method always bypasses the cache.
+ /// </remarks>
/// <param name="session">The session used to execute the
statement.</param>
/// <param name="parameterObject">The object used to set the
parameters in the SQL.</param>
/// <returns>The number of row effected.</returns>
@@ -226,6 +249,9 @@
/// Execute an insert statement. Fill the parameter object with
/// the ouput parameters if any, also could return the insert
generated key
/// </summary>
+ /// <remarks>
+ /// This method always bypasses the cache.
+ /// </remarks>
/// <param name="session">The session</param>
/// <param name="parameterObject">The parameter object used to
fill the statement.</param>
/// <returns>Can return the insert generated key.</returns>
@@ -239,6 +265,9 @@
/// <summary>
/// Executes the SQL and and fill a strongly typed collection.
/// </summary>
+ /// <remarks>
+ /// This method always bypasses the cache.
+ /// </remarks>
/// <param name="session">The session used to execute the
statement.</param>
/// <param name="parameterObject">The object used to set the
parameters in the SQL.</param>
/// <param name="resultObject">A strongly typed collection of
result objects.</param>
@@ -255,35 +284,33 @@
/// <returns>A List of result objects.</returns>
public IList ExecuteQueryForList(ISession session, object
parameterObject)
{
- IList list = null;
-
- object param = RaisePreEvent(PreSelectEventKey, parameterObject);
-
- RequestScope request = Statement.Sql.GetRequestScope(this, param,
session);
-
- mappedStatement.PreparedCommand.Create(request, session,
Statement, param);
-
- CacheKey cacheKey = GetCacheKey(request);
- cacheKey.Update("ExecuteQueryForList");
-
- bool cacheHit = true;
- list = Statement.CacheModel[cacheKey] as IList;
- if (list == null)
- {
- cacheHit = false;
- list = mappedStatement.RunQueryForList(request, session,
param, null, null);
- Statement.CacheModel[cacheKey] = list;
- }
+ // this doesn't need to be in its own RunQueryForCachedList method
because the class is sealed and can't be called by anyone else
+ RequestRunner<IList> requestRunner = delegate(RequestScope
requestScope, ISession session2, object parameter, CacheKey cacheKey, out bool
cacheHit)
+ {
+ cacheHit = true;
+ IList list = Statement.CacheModel[cacheKey] as IList;
+ if (list == null)
+ {
+ cacheHit = false;
+ list = mappedStatement.RunQueryForList(requestScope,
session, parameter, null, null);
+ Statement.CacheModel[cacheKey] = list;
+ }
+ return list;
+ };
- return RaisePostEvent(PostSelectEventKey, param, list, cacheHit);
+ return CachingStatementExecute(PreSelectEventKey,
PostSelectEventKey, session, parameterObject, "ExecuteQueryForList",
requestRunner);
}
- #endregion
+
+ #endregion
#region ExecuteQueryForList .NET 2.0
/// <summary>
/// Executes the SQL and and fill a strongly typed collection.
/// </summary>
+ /// <remarks>
+ /// This method always bypasses the cache.
+ /// </remarks>
/// <param name="session">The session used to execute the
statement.</param>
/// <param name="parameterObject">The object used to set the
parameters in the SQL.</param>
/// <param name="resultObject">A strongly typed collection of result
objects.</param>
@@ -300,27 +327,23 @@
/// <returns>A List of result objects.</returns>
public IList<T> ExecuteQueryForList<T>(ISession session, object
parameterObject)
{
- IList<T> list = null;
-
- object param = RaisePreEvent(PreSelectEventKey, parameterObject);
-
- RequestScope request = Statement.Sql.GetRequestScope(this, param,
session);
-
- mappedStatement.PreparedCommand.Create(request, session,
Statement, param);
-
- CacheKey cacheKey = GetCacheKey(request);
- cacheKey.Update("ExecuteQueryForList");
-
- bool cacheHit = true;
- list = Statement.CacheModel[cacheKey] as IList<T>;
- if (list == null)
+ // this doesn't need to be in its own RunQueryForCachedList method
because the class is sealed and can't be called by anyone else
+ RequestRunner<IList<T>> requestRunner = delegate(RequestScope
requestScope, ISession session2, object parameter, CacheKey cacheKey, out bool
cacheHit)
{
- cacheHit = false;
- list = mappedStatement.RunQueryForList<T>(request, session,
param, null, null);
- Statement.CacheModel[cacheKey] = list;
- }
- return RaisePostEvent(PostSelectEventKey, param, list, cacheHit);
+ cacheHit = true;
+ IList<T> list = Statement.CacheModel[cacheKey] as IList<T>;
+ if (list == null)
+ {
+ cacheHit = false;
+ list = mappedStatement.RunQueryForList<T>(requestScope,
session, parameter, null, null);
+ Statement.CacheModel[cacheKey] = list;
+ }
+ return list;
+ };
+
+ return CachingStatementExecute(PreSelectEventKey,
PostSelectEventKey, session, parameterObject, "ExecuteQueryForList",
requestRunner);
}
+
#endregion
#region ExecuteQueryForObject
@@ -335,33 +358,27 @@
/// <returns>The object</returns>
public object ExecuteQueryForObject(ISession session, object
parameterObject, object resultObject)
{
- object obj = null;
-
- object param = RaisePreEvent(PreSelectEventKey, parameterObject);
-
- RequestScope request = Statement.Sql.GetRequestScope(this, param,
session);
-
- mappedStatement.PreparedCommand.Create(request, session,
Statement, param);
-
- CacheKey cacheKey = GetCacheKey(request);
- cacheKey.Update("ExecuteQueryForObject");
-
- bool cacheHit = true;
- obj = Statement.CacheModel[cacheKey];
- // check if this query has alreay been run
- if (obj == CacheModel.NULL_OBJECT)
- {
- // convert the marker object back into a null
value
- obj = null;
- }
- else if (obj ==null)
- {
- cacheHit = false;
- obj = mappedStatement.RunQueryForObject(request, session,
param, resultObject);
- Statement.CacheModel[cacheKey] = obj;
- }
+ // this doesn't need to be in its own RunQueryForCachedObject
method because the class is sealed and can't be called by anyone else
+ RequestRunner<object> requestRunner = delegate(RequestScope
requestScope, ISession session2, object parameter, CacheKey cacheKey, out bool
cacheHit)
+ {
+ cacheHit = true;
+ object obj = Statement.CacheModel[cacheKey];
+ // check if this query has alreay been run
+ if (obj == CacheModel.NULL_OBJECT)
+ {
+ // convert the marker object back into a null value
+ obj = null;
+ }
+ else if (obj == null)
+ {
+ cacheHit = false;
+ obj = mappedStatement.RunQueryForObject(requestScope,
session, parameter, resultObject);
+ Statement.CacheModel[cacheKey] = obj;
+ }
+ return obj;
+ };
- return RaisePostEvent(PostSelectEventKey, param, obj, cacheHit);
+ return CachingStatementExecute(PreSelectEventKey,
PostSelectEventKey, session, parameterObject, "ExecuteQueryForObject",
requestRunner);
}
#endregion
@@ -378,38 +395,35 @@
/// <returns>The object</returns>
public T ExecuteQueryForObject<T>(ISession session, object
parameterObject, T resultObject)
{
- T obj; // = default(T);
-
- object param = RaisePreEvent(PreSelectEventKey, parameterObject);
-
- RequestScope request = Statement.Sql.GetRequestScope(this, param,
session);
-
- mappedStatement.PreparedCommand.Create(request, session,
Statement, param);
+ // this doesn't need to be in its own RunQueryForCachedObject
method because the class is sealed and can't be called by anyone else
+ RequestRunner<T> requestRunner = delegate(RequestScope
requestScope, ISession session2, object parameter, CacheKey cacheKey, out bool
cacheHit)
+ {
+ T obj;
- CacheKey cacheKey = GetCacheKey(request);
- cacheKey.Update("ExecuteQueryForObject");
+ cacheHit = false;
+ object cacheObjet = Statement.CacheModel[cacheKey];
+ // check if this query has alreay been run
+ if (cacheObjet is T)
+ {
+ cacheHit = true;
+ obj = (T)cacheObjet;
+ }
+ else if (cacheObjet == CacheModel.NULL_OBJECT)
+ {
+ // convert the marker object back into a null value
+ cacheHit = true;
+ obj = default(T);
+ }
+ else //if ((object)obj == null)
+ {
+ obj = mappedStatement.RunQueryForObject(requestScope,
session, parameter, resultObject);
+ Statement.CacheModel[cacheKey] = obj;
+ }
- bool cacheHit = false;
- object cacheObjet = Statement.CacheModel[cacheKey];
- // check if this query has alreay been run
- if (cacheObjet is T)
- {
- cacheHit = true;
- obj = (T)cacheObjet;
- }
- else if (cacheObjet == CacheModel.NULL_OBJECT)
- {
- // convert the marker object back into a null value
- cacheHit = true;
- obj = default(T);
- }
- else //if ((object)obj == null)
- {
- obj = (T)mappedStatement.RunQueryForObject(request, session,
param, resultObject);
- Statement.CacheModel[cacheKey] = obj;
- }
+ return obj;
+ };
- return RaisePostEvent(PostSelectEventKey, param, obj, cacheHit);
+ return CachingStatementExecute(PreSelectEventKey,
PostSelectEventKey, session, parameterObject, "ExecuteQueryForObject",
requestRunner);
}
#endregion
@@ -418,11 +432,15 @@
/// Runs a query with a custom object that gets a chance
/// to deal with each row as it is processed.
/// </summary>
+ /// <remarks>
+ /// This method always bypasses the cache.
+ /// </remarks>
/// <param name="session">The session used to execute the
statement.</param>
/// <param name="parameterObject">The object used to set the
parameters in the SQL.</param>
/// <param name="rowDelegate"></param>
public IList ExecuteQueryForRowDelegate(ISession session,
object parameterObject, RowDelegate rowDelegate)
{
+ // TODO: investigate allow the cached data to be processed by a
different rowDelegate...add rowDelegate to the CacheKey ???
return
mappedStatement.ExecuteQueryForRowDelegate(session, parameterObject,
rowDelegate);
}
@@ -430,18 +448,25 @@
/// Runs a query with a custom object that gets a chance
/// to deal with each row as it is processed.
/// </summary>
+ /// <remarks>
+ /// This method always bypasses the cache.
+ /// </remarks>
/// <param name="session">The session used to execute the
statement.</param>
/// <param name="parameterObject">The object used to set the
parameters in the SQL.</param>
/// <param name="rowDelegate"></param>
public IList<T> ExecuteQueryForRowDelegate<T>(ISession session, object
parameterObject, RowDelegate<T> rowDelegate)
{
- return mappedStatement.ExecuteQueryForRowDelegate<T>(session,
parameterObject, rowDelegate);
+ // TODO: investigate allow the cached data to be processed by a
different rowDelegate...add rowDelegate to the CacheKey ???
+ return mappedStatement.ExecuteQueryForRowDelegate(session,
parameterObject, rowDelegate);
}
/// <summary>
/// Runs a query with a custom object that gets a chance
/// to deal with each row as it is processed.
/// </summary>
+ /// <remarks>
+ /// This method always bypasses the cache.
+ /// </remarks>
/// <param name="session">The session used to execute the
statement</param>
/// <param name="parameterObject">The object used to set the
parameters in the SQL. </param>
/// <param name="keyProperty">The property of the result object
to be used as the key. </param>
@@ -451,6 +476,7 @@
/// <exception
cref="Apache.Ibatis.DataMapper.Exceptions.DataMapperException">If a transaction
is not in progress, or the database throws an exception.</exception>
public IDictionary ExecuteQueryForMapWithRowDelegate(ISession
session, object parameterObject, string keyProperty, string valueProperty,
DictionaryRowDelegate rowDelegate)
{
+ // TODO: investigate allow the cached data to be processed by a
different rowDelegate...add rowDelegate to the CacheKey ???
return
mappedStatement.ExecuteQueryForMapWithRowDelegate(session, parameterObject,
keyProperty, valueProperty, rowDelegate);
}
@@ -486,5 +512,27 @@
//}
return cacheKey;
}
+
+ /// <summary>
+ /// Ensures all the related Execute methods are run in a consistent
manner with pre and post events.
+ /// </summary>
+ /// <remarks>
+ /// Based off of MappedStatement.Execute
+ /// </remarks>
+ private T CachingStatementExecute<T>(object preEvent, object
postEvent, ISession session, object parameterObject, string baseCacheKey,
RequestRunner<T> requestRunner)
+ {
+ object paramPreEvent = RaisePreEvent(preEvent, parameterObject);
+
+ RequestScope requestScope = Statement.Sql.GetRequestScope(this,
paramPreEvent, session);
+ mappedStatement.PreparedCommand.Create(requestScope, session,
Statement, paramPreEvent);
+
+ CacheKey cacheKey = GetCacheKey(requestScope);
+ cacheKey.Update(baseCacheKey);
+
+ bool cacheHit;
+ T result = requestRunner(requestScope, session, paramPreEvent,
cacheKey, out cacheHit);
+
+ return RaisePostEvent(postEvent, paramPreEvent, result, cacheHit);
+ }
}
}