/src/NHibernate/Loader/Custom/Sql/SQLQueryReturnProcessor.cs

https://github.com/RogerKratz/nhibernate-core · C# · 410 lines · 353 code · 49 blank · 8 comment · 49 complexity · fee1d49fb7cb1a7598b5b2d55ca31bad MD5 · raw file

  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using NHibernate.Engine;
  5. using NHibernate.Engine.Query.Sql;
  6. using NHibernate.Persister.Collection;
  7. using NHibernate.Persister.Entity;
  8. using NHibernate.Type;
  9. namespace NHibernate.Loader.Custom.Sql
  10. {
  11. public class SQLQueryReturnProcessor
  12. {
  13. private static readonly INHibernateLogger log = NHibernateLogger.For(typeof (SQLQueryReturnProcessor));
  14. private readonly INativeSQLQueryReturn[] queryReturns;
  15. private readonly Dictionary<string, INativeSQLQueryReturn> alias2Return =
  16. new Dictionary<string, INativeSQLQueryReturn>();
  17. private readonly Dictionary<string, string> alias2OwnerAlias = new Dictionary<string, string>();
  18. private readonly Dictionary<string, ISqlLoadable> alias2Persister = new Dictionary<string, ISqlLoadable>();
  19. private readonly Dictionary<string, string> alias2Suffix = new Dictionary<string, string>();
  20. private readonly Dictionary<string, ISqlLoadableCollection> alias2CollectionPersister =
  21. new Dictionary<string, ISqlLoadableCollection>();
  22. private readonly Dictionary<string, string> alias2CollectionSuffix = new Dictionary<string, string>();
  23. private readonly Dictionary<string, IDictionary<string, string[]>> entityPropertyResultMaps =
  24. new Dictionary<string, IDictionary<string, string[]>>();
  25. private readonly Dictionary<string, IDictionary<string, string[]>> collectionPropertyResultMaps =
  26. new Dictionary<string, IDictionary<string, string[]>>();
  27. private readonly ISessionFactoryImplementor factory;
  28. private int entitySuffixSeed = 0;
  29. private int collectionSuffixSeed = 0;
  30. private ISessionFactoryImplementor Factory
  31. {
  32. get { return factory; }
  33. }
  34. public SQLQueryReturnProcessor(INativeSQLQueryReturn[] queryReturns, ISessionFactoryImplementor factory)
  35. {
  36. this.queryReturns = queryReturns;
  37. this.factory = factory;
  38. }
  39. public class ResultAliasContext
  40. {
  41. private readonly SQLQueryReturnProcessor parent;
  42. public ResultAliasContext(SQLQueryReturnProcessor parent)
  43. {
  44. this.parent = parent;
  45. }
  46. public ISqlLoadable GetEntityPersister(string alias)
  47. {
  48. ISqlLoadable result;
  49. parent.alias2Persister.TryGetValue(alias, out result);
  50. return result;
  51. }
  52. public ISqlLoadableCollection GetCollectionPersister(string alias)
  53. {
  54. ISqlLoadableCollection result;
  55. parent.alias2CollectionPersister.TryGetValue(alias, out result);
  56. return result;
  57. }
  58. public string GetEntitySuffix(string alias)
  59. {
  60. string result;
  61. parent.alias2Suffix.TryGetValue(alias, out result);
  62. return result;
  63. }
  64. public string GetCollectionSuffix(string alias)
  65. {
  66. string result;
  67. parent.alias2CollectionSuffix.TryGetValue(alias, out result);
  68. return result;
  69. }
  70. public string GetOwnerAlias(string alias)
  71. {
  72. string result;
  73. parent.alias2OwnerAlias.TryGetValue(alias, out result);
  74. return result;
  75. }
  76. public IDictionary<string, string[]> GetPropertyResultsMap(string alias)
  77. {
  78. return parent.InternalGetPropertyResultsMap(alias);
  79. }
  80. }
  81. private IDictionary<string, string[]> InternalGetPropertyResultsMap(string alias)
  82. {
  83. NativeSQLQueryNonScalarReturn rtn = alias2Return[alias] as NativeSQLQueryNonScalarReturn;
  84. return rtn != null ? rtn.PropertyResultsMap : null;
  85. }
  86. private bool HasPropertyResultMap(string alias)
  87. {
  88. IDictionary<string, string[]> propertyMaps = InternalGetPropertyResultsMap(alias);
  89. return propertyMaps != null && propertyMaps.Count != 0;
  90. }
  91. public ResultAliasContext Process()
  92. {
  93. // first, break down the returns into maps keyed by alias
  94. // so that role returns can be more easily resolved to their owners
  95. for (int i = 0; i < queryReturns.Length; i++)
  96. {
  97. var rtn = queryReturns[i] as NativeSQLQueryNonScalarReturn;
  98. if (rtn != null)
  99. {
  100. alias2Return[rtn.Alias] = rtn;
  101. var roleReturn = queryReturns[i] as NativeSQLQueryJoinReturn;
  102. if (roleReturn != null)
  103. {
  104. alias2OwnerAlias[roleReturn.Alias] = roleReturn.OwnerAlias;
  105. }
  106. }
  107. }
  108. // Now, process the returns
  109. for (int i = 0; i < queryReturns.Length; i++)
  110. {
  111. ProcessReturn(queryReturns[i]);
  112. }
  113. return new ResultAliasContext(this);
  114. }
  115. private ISqlLoadable GetSQLLoadable(string entityName)
  116. {
  117. IEntityPersister persister = factory.GetEntityPersister(entityName);
  118. var persisterAsSqlLoadable = persister as ISqlLoadable;
  119. if (persisterAsSqlLoadable == null)
  120. {
  121. throw new MappingException("class persister is not ISqlLoadable: " + entityName);
  122. }
  123. return persisterAsSqlLoadable;
  124. }
  125. private string GenerateEntitySuffix()
  126. {
  127. return BasicLoader.GenerateSuffixes(entitySuffixSeed++, 1)[0];
  128. }
  129. private string GenerateCollectionSuffix()
  130. {
  131. return collectionSuffixSeed++ + "__";
  132. }
  133. private void ProcessReturn(INativeSQLQueryReturn rtn)
  134. {
  135. if (rtn is NativeSQLQueryScalarReturn)
  136. {
  137. ProcessScalarReturn((NativeSQLQueryScalarReturn) rtn);
  138. }
  139. else if (rtn is NativeSQLQueryRootReturn)
  140. {
  141. ProcessRootReturn((NativeSQLQueryRootReturn) rtn);
  142. }
  143. else if (rtn is NativeSQLQueryCollectionReturn)
  144. {
  145. ProcessCollectionReturn((NativeSQLQueryCollectionReturn) rtn);
  146. }
  147. else
  148. {
  149. ProcessJoinReturn((NativeSQLQueryJoinReturn) rtn);
  150. }
  151. }
  152. private void ProcessScalarReturn(NativeSQLQueryScalarReturn typeReturn) {}
  153. private void ProcessRootReturn(NativeSQLQueryRootReturn rootReturn)
  154. {
  155. if (alias2Persister.ContainsKey(rootReturn.Alias))
  156. {
  157. // already been processed...
  158. return;
  159. }
  160. ISqlLoadable persister = GetSQLLoadable(rootReturn.ReturnEntityName);
  161. AddPersister(rootReturn.Alias, rootReturn.PropertyResultsMap, persister);
  162. }
  163. private void AddPersister(string alias, IDictionary<string, string[]> propertyResult, ISqlLoadable persister)
  164. {
  165. alias2Persister[alias] = persister;
  166. string suffix = GenerateEntitySuffix();
  167. log.Debug("mapping alias [{0}] to entity-suffix [{1}]", alias, suffix);
  168. alias2Suffix[alias] = suffix;
  169. entityPropertyResultMaps[alias] = propertyResult;
  170. }
  171. private void AddCollection(string role, string alias, IDictionary<string, string[]> propertyResults)
  172. {
  173. ISqlLoadableCollection collectionPersister = (ISqlLoadableCollection) Factory.GetCollectionPersister(role);
  174. alias2CollectionPersister[alias] = collectionPersister;
  175. string suffix = GenerateCollectionSuffix();
  176. log.Debug("mapping alias [{0}] to collection-suffix [{1}]", alias, suffix);
  177. alias2CollectionSuffix[alias] = suffix;
  178. collectionPropertyResultMaps[alias] = propertyResults;
  179. if (collectionPersister.IsOneToMany)
  180. {
  181. ISqlLoadable persister = (ISqlLoadable) collectionPersister.ElementPersister;
  182. AddPersister(alias, Filter(propertyResults), persister);
  183. }
  184. }
  185. private IDictionary<string, string[]> Filter(IDictionary<string, string[]> propertyResults)
  186. {
  187. Dictionary<string, string[]> result = new Dictionary<string, string[]>(propertyResults.Count);
  188. string keyPrefix = "element.";
  189. foreach (KeyValuePair<string, string[]> element in propertyResults)
  190. {
  191. string path = element.Key;
  192. if (path.StartsWith(keyPrefix, StringComparison.Ordinal))
  193. {
  194. result[path.Substring(keyPrefix.Length)] = element.Value;
  195. }
  196. }
  197. return result;
  198. }
  199. private void ProcessCollectionReturn(NativeSQLQueryCollectionReturn collectionReturn)
  200. {
  201. // we are initializing an owned collection
  202. string role = collectionReturn.OwnerEntityName + '.' + collectionReturn.OwnerProperty;
  203. AddCollection(role, collectionReturn.Alias, collectionReturn.PropertyResultsMap);
  204. }
  205. private void ProcessJoinReturn(NativeSQLQueryJoinReturn fetchReturn)
  206. {
  207. string alias = fetchReturn.Alias;
  208. if (alias2Persister.ContainsKey(alias) || alias2CollectionPersister.ContainsKey(alias))
  209. {
  210. // already been processed...
  211. return;
  212. }
  213. string ownerAlias = fetchReturn.OwnerAlias;
  214. // Make sure the owner alias is known...
  215. if (!alias2Return.ContainsKey(ownerAlias))
  216. {
  217. throw new HibernateException(string.Format("Owner alias [{0}] is unknown for alias [{1}]", ownerAlias, alias));
  218. }
  219. // If this return's alias has not been processed yet, do so b4 further processing of this return
  220. if (!alias2Persister.ContainsKey(ownerAlias))
  221. {
  222. ProcessReturn(alias2Return[ownerAlias]);
  223. }
  224. ISqlLoadable ownerPersister = alias2Persister[ownerAlias];
  225. IType returnType = ownerPersister.GetPropertyType(fetchReturn.OwnerProperty);
  226. if (returnType.IsCollectionType)
  227. {
  228. string role = ownerPersister.EntityName + '.' + fetchReturn.OwnerProperty;
  229. AddCollection(role, alias, fetchReturn.PropertyResultsMap);
  230. }
  231. else if (returnType.IsEntityType)
  232. {
  233. EntityType eType = (EntityType) returnType;
  234. string returnEntityName = eType.GetAssociatedEntityName();
  235. ISqlLoadable persister = GetSQLLoadable(returnEntityName);
  236. AddPersister(alias, fetchReturn.PropertyResultsMap, persister);
  237. }
  238. }
  239. public IList GenerateCustomReturns(bool queryHadAliases)
  240. {
  241. IList customReturns = new List<object>();
  242. IDictionary<string, object> customReturnsByAlias = new Dictionary<string, object>();
  243. for (int i = 0; i < queryReturns.Length; i++)
  244. {
  245. if (queryReturns[i] is NativeSQLQueryScalarReturn)
  246. {
  247. NativeSQLQueryScalarReturn rtn = (NativeSQLQueryScalarReturn) queryReturns[i];
  248. customReturns.Add(new ScalarReturn(rtn.Type, rtn.ColumnAlias));
  249. }
  250. else if (queryReturns[i] is NativeSQLQueryRootReturn)
  251. {
  252. NativeSQLQueryRootReturn rtn = (NativeSQLQueryRootReturn) queryReturns[i];
  253. string alias = rtn.Alias;
  254. IEntityAliases entityAliases;
  255. if (queryHadAliases || HasPropertyResultMap(alias))
  256. {
  257. entityAliases =
  258. new DefaultEntityAliases(entityPropertyResultMaps[alias], alias2Persister[alias], alias2Suffix[alias]);
  259. }
  260. else
  261. {
  262. entityAliases =
  263. new ColumnEntityAliases(entityPropertyResultMaps[alias], alias2Persister[alias], alias2Suffix[alias]);
  264. }
  265. RootReturn customReturn = new RootReturn(alias, rtn.ReturnEntityName, entityAliases, rtn.LockMode);
  266. customReturns.Add(customReturn);
  267. customReturnsByAlias[rtn.Alias] = customReturn;
  268. }
  269. else if (queryReturns[i] is NativeSQLQueryCollectionReturn)
  270. {
  271. NativeSQLQueryCollectionReturn rtn = (NativeSQLQueryCollectionReturn) queryReturns[i];
  272. string alias = rtn.Alias;
  273. ISqlLoadableCollection persister = alias2CollectionPersister[alias];
  274. bool isEntityElements = persister.ElementType.IsEntityType;
  275. ICollectionAliases collectionAliases;
  276. IEntityAliases elementEntityAliases = null;
  277. if (queryHadAliases || HasPropertyResultMap(alias))
  278. {
  279. collectionAliases =
  280. new GeneratedCollectionAliases(collectionPropertyResultMaps[alias], alias2CollectionPersister[alias],
  281. alias2CollectionSuffix[alias]);
  282. if (isEntityElements)
  283. {
  284. elementEntityAliases =
  285. new DefaultEntityAliases(entityPropertyResultMaps[alias], alias2Persister[alias], alias2Suffix[alias]);
  286. }
  287. }
  288. else
  289. {
  290. collectionAliases =
  291. new ColumnCollectionAliases(collectionPropertyResultMaps[alias], alias2CollectionPersister[alias]);
  292. if (isEntityElements)
  293. {
  294. elementEntityAliases =
  295. new ColumnEntityAliases(entityPropertyResultMaps[alias], alias2Persister[alias], alias2Suffix[alias]);
  296. }
  297. }
  298. CollectionReturn customReturn =
  299. new CollectionReturn(alias, rtn.OwnerEntityName, rtn.OwnerProperty, collectionAliases, elementEntityAliases,
  300. rtn.LockMode);
  301. customReturns.Add(customReturn);
  302. customReturnsByAlias[rtn.Alias] = customReturn;
  303. }
  304. else if (queryReturns[i] is NativeSQLQueryJoinReturn)
  305. {
  306. NativeSQLQueryJoinReturn rtn = (NativeSQLQueryJoinReturn) queryReturns[i];
  307. string alias = rtn.Alias;
  308. FetchReturn customReturn;
  309. NonScalarReturn ownerCustomReturn = (NonScalarReturn) customReturnsByAlias[rtn.OwnerAlias];
  310. ISqlLoadableCollection persister;
  311. if (alias2CollectionPersister.TryGetValue(alias, out persister))
  312. {
  313. bool isEntityElements = persister.ElementType.IsEntityType;
  314. ICollectionAliases collectionAliases;
  315. IEntityAliases elementEntityAliases = null;
  316. if (queryHadAliases || HasPropertyResultMap(alias))
  317. {
  318. collectionAliases =
  319. new GeneratedCollectionAliases(collectionPropertyResultMaps[alias], persister, alias2CollectionSuffix[alias]);
  320. if (isEntityElements)
  321. {
  322. elementEntityAliases =
  323. new DefaultEntityAliases(entityPropertyResultMaps[alias], alias2Persister[alias], alias2Suffix[alias]);
  324. }
  325. }
  326. else
  327. {
  328. collectionAliases = new ColumnCollectionAliases(collectionPropertyResultMaps[alias], persister);
  329. if (isEntityElements)
  330. {
  331. elementEntityAliases =
  332. new ColumnEntityAliases(entityPropertyResultMaps[alias], alias2Persister[alias], alias2Suffix[alias]);
  333. }
  334. }
  335. customReturn =
  336. new CollectionFetchReturn(alias, ownerCustomReturn, rtn.OwnerProperty, collectionAliases, elementEntityAliases,
  337. rtn.LockMode);
  338. }
  339. else
  340. {
  341. IEntityAliases entityAliases;
  342. if (queryHadAliases || HasPropertyResultMap(alias))
  343. {
  344. entityAliases =
  345. new DefaultEntityAliases(entityPropertyResultMaps[alias], alias2Persister[alias], alias2Suffix[alias]);
  346. }
  347. else
  348. {
  349. entityAliases =
  350. new ColumnEntityAliases(entityPropertyResultMaps[alias], alias2Persister[alias], alias2Suffix[alias]);
  351. }
  352. customReturn = new EntityFetchReturn(alias, entityAliases, ownerCustomReturn, rtn.OwnerProperty, rtn.LockMode);
  353. }
  354. customReturns.Add(customReturn);
  355. customReturnsByAlias[alias] = customReturn;
  356. }
  357. }
  358. return customReturns;
  359. }
  360. }
  361. }