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);
+        }
     }
 }


Reply via email to