/src/NHibernate/Engine/Query/QueryPlanCache.cs

https://github.com/Kengutoudegou/nhibernate-core · C# · 309 lines · 251 code · 45 blank · 13 comment · 44 complexity · ee6749164b2b93631526986562546d0a MD5 · raw file

  1. using System;
  2. using System.Collections.Generic;
  3. using Iesi.Collections.Generic;
  4. using NHibernate.Engine.Query.Sql;
  5. using NHibernate.Util;
  6. namespace NHibernate.Engine.Query
  7. {
  8. /// <summary> Acts as a cache for compiled query plans, as well as query-parameter metadata. </summary>
  9. [Serializable]
  10. public class QueryPlanCache
  11. {
  12. private static readonly IInternalLogger log = LoggerProvider.LoggerFor(typeof(QueryPlanCache));
  13. private readonly ISessionFactoryImplementor factory;
  14. // simple cache of param metadata based on query string. Ideally, the
  15. // original "user-supplied query" string should be used to retrieve this
  16. // metadata (i.e., not the para-list-expanded query string) to avoid
  17. // unnecessary cache entries.
  18. // Used solely for caching param metadata for native-sql queries, see
  19. // getSQLParameterMetadata() for a discussion as to why...
  20. private readonly SimpleMRUCache sqlParamMetadataCache = new SimpleMRUCache();
  21. // the cache of the actual plans...
  22. private readonly SoftLimitMRUCache planCache = new SoftLimitMRUCache(128);
  23. public QueryPlanCache(ISessionFactoryImplementor factory)
  24. {
  25. this.factory = factory;
  26. }
  27. public ParameterMetadata GetSQLParameterMetadata(string query)
  28. {
  29. var metadata = (ParameterMetadata)sqlParamMetadataCache[query];
  30. if (metadata == null)
  31. {
  32. // for native-sql queries, the param metadata is determined outside
  33. // any relation to a query plan, because query plan creation and/or
  34. // retrieval for a native-sql query depends on all of the return
  35. // types having been set, which might not be the case up-front when
  36. // param metadata would be most useful
  37. metadata = BuildNativeSQLParameterMetadata(query);
  38. sqlParamMetadataCache.Put(query, metadata);
  39. }
  40. return metadata;
  41. }
  42. public IQueryPlan GetHQLQueryPlan(string queryString, bool shallow, IDictionary<string, IFilter> enabledFilters)
  43. {
  44. var key = new HQLQueryPlanKey(queryString, shallow, enabledFilters);
  45. var plan = (IQueryPlan)planCache[key];
  46. if (plan == null)
  47. {
  48. if (log.IsDebugEnabled)
  49. {
  50. log.Debug("unable to locate HQL query plan in cache; generating (" + queryString + ")");
  51. }
  52. plan = new HQLStringQueryPlan(queryString, shallow, enabledFilters, factory);
  53. planCache.Put(key, plan);
  54. }
  55. else
  56. {
  57. if (log.IsDebugEnabled)
  58. {
  59. log.Debug("located HQL query plan in cache (" + queryString + ")");
  60. }
  61. }
  62. return plan;
  63. }
  64. public IQueryExpressionPlan GetHQLQueryPlan(IQueryExpression queryExpression, bool shallow, IDictionary<string, IFilter> enabledFilters)
  65. {
  66. string expressionStr = queryExpression.Key;
  67. var key = new HQLQueryPlanKey(expressionStr, shallow, enabledFilters);
  68. var plan = (IQueryExpressionPlan)planCache[key];
  69. if (plan == null)
  70. {
  71. if (log.IsDebugEnabled)
  72. {
  73. log.Debug("unable to locate HQL query plan in cache; generating (" + expressionStr + ")");
  74. }
  75. plan = new HQLExpressionQueryPlan(expressionStr, queryExpression, shallow, enabledFilters, factory);
  76. planCache.Put(key, plan);
  77. }
  78. else
  79. {
  80. if (log.IsDebugEnabled)
  81. {
  82. log.Debug("located HQL query plan in cache (" + expressionStr + ")");
  83. }
  84. }
  85. return plan;
  86. }
  87. public FilterQueryPlan GetFilterQueryPlan(string filterString, string collectionRole, bool shallow, IDictionary<string, IFilter> enabledFilters)
  88. {
  89. var key = new FilterQueryPlanKey(filterString, collectionRole, shallow, enabledFilters);
  90. var plan = (FilterQueryPlan) planCache[key];
  91. if (plan == null)
  92. {
  93. if (log.IsDebugEnabled)
  94. {
  95. log.Debug("unable to locate collection-filter query plan in cache; generating (" + collectionRole + " : "
  96. + filterString + ")");
  97. }
  98. plan = new FilterQueryPlan(filterString, collectionRole, shallow, enabledFilters, factory);
  99. planCache.Put(key, plan);
  100. }
  101. else
  102. {
  103. if (log.IsDebugEnabled)
  104. {
  105. log.Debug("located collection-filter query plan in cache (" + collectionRole + " : " + filterString + ")");
  106. }
  107. }
  108. return plan;
  109. }
  110. public NativeSQLQueryPlan GetNativeSQLQueryPlan(NativeSQLQuerySpecification spec)
  111. {
  112. var plan = (NativeSQLQueryPlan)planCache[spec];
  113. if (plan == null)
  114. {
  115. if (log.IsDebugEnabled)
  116. {
  117. log.Debug("unable to locate native-sql query plan in cache; generating (" + spec.QueryString + ")");
  118. }
  119. plan = new NativeSQLQueryPlan(spec, factory);
  120. planCache.Put(spec, plan);
  121. }
  122. else
  123. {
  124. if (log.IsDebugEnabled)
  125. {
  126. log.Debug("located native-sql query plan in cache (" + spec.QueryString + ")");
  127. }
  128. }
  129. return plan;
  130. }
  131. private ParameterMetadata BuildNativeSQLParameterMetadata(string sqlString)
  132. {
  133. ParamLocationRecognizer recognizer = ParamLocationRecognizer.ParseLocations(sqlString);
  134. var ordinalDescriptors = new OrdinalParameterDescriptor[recognizer.OrdinalParameterLocationList.Count];
  135. for (int i = 0; i < recognizer.OrdinalParameterLocationList.Count; i++)
  136. {
  137. int position = recognizer.OrdinalParameterLocationList[i];
  138. ordinalDescriptors[i] = new OrdinalParameterDescriptor(i, null);
  139. }
  140. IDictionary<string, NamedParameterDescriptor> namedParamDescriptorMap = new Dictionary<string, NamedParameterDescriptor>();
  141. foreach (KeyValuePair<string, ParamLocationRecognizer.NamedParameterDescription> entry in recognizer.NamedParameterDescriptionMap)
  142. {
  143. string name = entry.Key;
  144. ParamLocationRecognizer.NamedParameterDescription description = entry.Value;
  145. namedParamDescriptorMap[name] =
  146. new NamedParameterDescriptor(name, null, description.JpaStyle);
  147. }
  148. return new ParameterMetadata(ordinalDescriptors, namedParamDescriptorMap);
  149. }
  150. [Serializable]
  151. private class HQLQueryPlanKey
  152. {
  153. private readonly string query;
  154. private readonly bool shallow;
  155. private readonly ISet<string> filterNames;
  156. private readonly int hashCode;
  157. public HQLQueryPlanKey(string query, bool shallow, IDictionary<string, IFilter> enabledFilters)
  158. {
  159. this.query = query;
  160. this.shallow = shallow;
  161. if (enabledFilters == null || (enabledFilters.Count == 0))
  162. {
  163. filterNames = new HashedSet<string>();
  164. }
  165. else
  166. {
  167. filterNames = new HashedSet<string>(enabledFilters.Keys);
  168. }
  169. int hash = query.GetHashCode();
  170. hash = 29 * hash + (shallow ? 1 : 0);
  171. hash = 29 * hash + CollectionHelper.GetHashCode(filterNames);
  172. hashCode = hash;
  173. }
  174. public override bool Equals(object obj)
  175. {
  176. return this == obj || Equals(obj as HQLQueryPlanKey);
  177. }
  178. public bool Equals(HQLQueryPlanKey that)
  179. {
  180. if (that == null)
  181. {
  182. return false;
  183. }
  184. if (shallow != that.shallow)
  185. {
  186. return false;
  187. }
  188. if (!CollectionHelper.SetEquals(filterNames, that.filterNames))
  189. {
  190. return false;
  191. }
  192. if (!query.Equals(that.query))
  193. {
  194. return false;
  195. }
  196. return true;
  197. }
  198. public override int GetHashCode()
  199. {
  200. return hashCode;
  201. }
  202. }
  203. [Serializable]
  204. private class FilterQueryPlanKey
  205. {
  206. private readonly string query;
  207. private readonly string collectionRole;
  208. private readonly bool shallow;
  209. private readonly ISet<string> filterNames;
  210. private readonly int hashCode;
  211. public FilterQueryPlanKey(string query, string collectionRole, bool shallow, IDictionary<string, IFilter> enabledFilters)
  212. {
  213. this.query = query;
  214. this.collectionRole = collectionRole;
  215. this.shallow = shallow;
  216. if (enabledFilters == null || (enabledFilters.Count == 0))
  217. {
  218. filterNames = new HashedSet<string>();
  219. }
  220. else
  221. {
  222. filterNames = new HashedSet<string>(enabledFilters.Keys);
  223. }
  224. int hash = query.GetHashCode();
  225. hash = 29 * hash + collectionRole.GetHashCode();
  226. hash = 29 * hash + (shallow ? 1 : 0);
  227. hash = 29 * hash + CollectionHelper.GetHashCode(filterNames);
  228. hashCode = hash;
  229. }
  230. public override bool Equals(object obj)
  231. {
  232. return this == obj || Equals(obj as FilterQueryPlanKey);
  233. }
  234. public bool Equals(FilterQueryPlanKey that)
  235. {
  236. if (that == null)
  237. {
  238. return false;
  239. }
  240. if (shallow != that.shallow)
  241. {
  242. return false;
  243. }
  244. if (!CollectionHelper.SetEquals(filterNames, that.filterNames))
  245. {
  246. return false;
  247. }
  248. if (!query.Equals(that.query))
  249. {
  250. return false;
  251. }
  252. if (!collectionRole.Equals(that.collectionRole))
  253. {
  254. return false;
  255. }
  256. return true;
  257. }
  258. public override int GetHashCode()
  259. {
  260. return hashCode;
  261. }
  262. }
  263. }
  264. }