PageRenderTime 66ms CodeModel.GetById 7ms RepoModel.GetById 1ms app.codeStats 0ms

/src/NHibernate/Loader/Loader.cs

https://github.com/okb/nhibernate-core
C# | 1761 lines | 1188 code | 219 blank | 354 comment | 195 complexity | ad7244b9a67ca07b9ac136df746ce830 MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, LGPL-2.1, MPL-2.0-no-copyleft-exception, LGPL-3.0, Apache-2.0, CC-BY-SA-3.0

Large files files are truncated, but you can click here to view the full file

  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Data;
  5. using System.Diagnostics;
  6. using System.Linq;
  7. using System.Runtime.CompilerServices;
  8. using Iesi.Collections;
  9. using Iesi.Collections.Generic;
  10. using NHibernate.AdoNet;
  11. using NHibernate.Cache;
  12. using NHibernate.Collection;
  13. using NHibernate.Driver;
  14. using NHibernate.Engine;
  15. using NHibernate.Event;
  16. using NHibernate.Exceptions;
  17. using NHibernate.Hql.Classic;
  18. using NHibernate.Hql.Util;
  19. using NHibernate.Impl;
  20. using NHibernate.Param;
  21. using NHibernate.Persister.Collection;
  22. using NHibernate.Persister.Entity;
  23. using NHibernate.Proxy;
  24. using NHibernate.SqlCommand;
  25. using NHibernate.Transform;
  26. using NHibernate.Type;
  27. using NHibernate.Util;
  28. namespace NHibernate.Loader
  29. {
  30. /// <summary>
  31. /// Abstract superclass of object loading (and querying) strategies.
  32. /// </summary>
  33. /// <remarks>
  34. /// <p>
  35. /// This class implements useful common functionality that concrete loaders would delegate to.
  36. /// It is not intended that this functionality would be directly accessed by client code (Hence,
  37. /// all methods of this class are declared <c>protected</c> or <c>private</c>.) This class relies heavily upon the
  38. /// <see cref="ILoadable" /> interface, which is the contract between this class and
  39. /// <see cref="IEntityPersister" />s that may be loaded by it.
  40. /// </p>
  41. /// <p>
  42. /// The present implementation is able to load any number of columns of entities and at most
  43. /// one collection role per query.
  44. /// </p>
  45. /// </remarks>
  46. /// <seealso cref="NHibernate.Persister.Entity.ILoadable"/>
  47. public abstract class Loader
  48. {
  49. private static readonly IInternalLogger Log = LoggerProvider.LoggerFor(typeof(Loader));
  50. private readonly ISessionFactoryImplementor _factory;
  51. private readonly SessionFactoryHelper _helper;
  52. private ColumnNameCache _columnNameCache;
  53. protected Loader(ISessionFactoryImplementor factory)
  54. {
  55. _factory = factory;
  56. _helper = new SessionFactoryHelper(factory);
  57. }
  58. protected SessionFactoryHelper Helper
  59. {
  60. get { return _helper; }
  61. }
  62. /// <summary>
  63. /// An array indicating whether the entities have eager property fetching
  64. /// enabled.
  65. /// </summary>
  66. /// <value> Eager property fetching indicators. </value>
  67. protected virtual bool[] EntityEagerPropertyFetches
  68. {
  69. get { return null; }
  70. }
  71. /// <summary>
  72. /// An array of indexes of the entity that owns a one-to-one association
  73. /// to the entity at the given index (-1 if there is no "owner")
  74. /// </summary>
  75. /// <remarks>
  76. /// The indexes contained here are relative to the result of <see cref="EntityPersisters"/>.
  77. /// </remarks>
  78. protected virtual int[] Owners
  79. {
  80. get { return null; }
  81. }
  82. /// <summary>
  83. /// An array of the owner types corresponding to the <see cref="Owners"/>
  84. /// returns. Indices indicating no owner would be null here.
  85. /// </summary>
  86. protected virtual EntityType[] OwnerAssociationTypes
  87. {
  88. get { return null; }
  89. }
  90. /// <summary>
  91. /// Get the index of the entity that owns the collection, or -1
  92. /// if there is no owner in the query results (i.e. in the case of a
  93. /// collection initializer) or no collection.
  94. /// </summary>
  95. protected virtual int[] CollectionOwners
  96. {
  97. get { return null; }
  98. }
  99. /// <summary>
  100. /// Return false is this loader is a batch entity loader
  101. /// </summary>
  102. protected virtual bool IsSingleRowLoader
  103. {
  104. get { return false; }
  105. }
  106. public virtual bool IsSubselectLoadingEnabled
  107. {
  108. get { return false; }
  109. }
  110. /// <summary>
  111. /// Get the result set descriptor
  112. /// </summary>
  113. protected abstract IEntityAliases[] EntityAliases { get; }
  114. protected abstract ICollectionAliases[] CollectionAliases { get; }
  115. public ISessionFactoryImplementor Factory
  116. {
  117. get { return _factory; }
  118. }
  119. /// <summary>
  120. /// The SqlString to be called; implemented by all subclasses
  121. /// </summary>
  122. /// <remarks>
  123. /// <para>
  124. /// The <c>setter</c> was added so that class inheriting from Loader could write a
  125. /// value using the Property instead of directly to the field.
  126. /// </para>
  127. /// <para>
  128. /// The scope is <c>protected internal</c> because the <see cref="Hql.Classic.WhereParser"/> needs to
  129. /// be able to <c>get</c> the SqlString of the <see cref="Hql.Classic.QueryTranslator"/> when
  130. /// it is parsing a subquery.
  131. /// </para>
  132. /// </remarks>
  133. public abstract SqlString SqlString { get; }
  134. /// <summary>
  135. /// An array of persisters of entity classes contained in each row of results;
  136. /// implemented by all subclasses
  137. /// </summary>
  138. /// <remarks>
  139. /// The <c>setter</c> was added so that classes inheriting from Loader could write a
  140. /// value using the Property instead of directly to the field.
  141. /// </remarks>
  142. public abstract ILoadable[] EntityPersisters { get; }
  143. /// <summary>
  144. /// An (optional) persister for a collection to be initialized; only collection loaders
  145. /// return a non-null value
  146. /// </summary>
  147. protected virtual ICollectionPersister[] CollectionPersisters
  148. {
  149. get { return null; }
  150. }
  151. /// <summary>
  152. /// What lock mode does this load entities with?
  153. /// </summary>
  154. /// <param name="lockModes">A Collection of lock modes specified dynamically via the Query Interface</param>
  155. /// <returns></returns>
  156. public abstract LockMode[] GetLockModes(IDictionary<string, LockMode> lockModes);
  157. /// <summary>
  158. /// Append <c>FOR UPDATE OF</c> clause, if necessary. This
  159. /// empty superclass implementation merely returns its first
  160. /// argument.
  161. /// </summary>
  162. protected virtual SqlString ApplyLocks(SqlString sql, IDictionary<string, LockMode> lockModes, Dialect.Dialect dialect)
  163. {
  164. return sql;
  165. }
  166. /// <summary>
  167. /// Does this query return objects that might be already cached by
  168. /// the session, whose lock mode may need upgrading.
  169. /// </summary>
  170. /// <returns></returns>
  171. protected virtual bool UpgradeLocks()
  172. {
  173. return false;
  174. }
  175. /// <summary>
  176. /// Get the SQL table aliases of entities whose
  177. /// associations are subselect-loadable, returning
  178. /// null if this loader does not support subselect
  179. /// loading
  180. /// </summary>
  181. protected virtual string[] Aliases
  182. {
  183. get { return null; }
  184. }
  185. /// <summary>
  186. /// Modify the SQL, adding lock hints and comments, if necessary
  187. /// </summary>
  188. protected virtual SqlString PreprocessSQL(SqlString sql, QueryParameters parameters, Dialect.Dialect dialect)
  189. {
  190. sql = ApplyLocks(sql, parameters.LockModes, dialect);
  191. return Factory.Settings.IsCommentsEnabled ? PrependComment(sql, parameters) : sql;
  192. }
  193. private static SqlString PrependComment(SqlString sql, QueryParameters parameters)
  194. {
  195. string comment = parameters.Comment;
  196. if (string.IsNullOrEmpty(comment))
  197. {
  198. return sql;
  199. }
  200. else
  201. {
  202. return sql.Insert(0, "/* " + comment + " */");
  203. }
  204. }
  205. /// <summary>
  206. /// Execute an SQL query and attempt to instantiate instances of the class mapped by the given
  207. /// persister from each row of the <c>DataReader</c>. If an object is supplied, will attempt to
  208. /// initialize that object. If a collection is supplied, attempt to initialize that collection.
  209. /// </summary>
  210. private IList DoQueryAndInitializeNonLazyCollections(ISessionImplementor session, QueryParameters queryParameters, bool returnProxies)
  211. {
  212. IPersistenceContext persistenceContext = session.PersistenceContext;
  213. bool defaultReadOnlyOrig = persistenceContext.DefaultReadOnly;
  214. if (queryParameters.IsReadOnlyInitialized)
  215. persistenceContext.DefaultReadOnly = queryParameters.ReadOnly;
  216. else
  217. queryParameters.ReadOnly = persistenceContext.DefaultReadOnly;
  218. persistenceContext.BeforeLoad();
  219. IList result;
  220. try
  221. {
  222. try
  223. {
  224. result = DoQuery(session, queryParameters, returnProxies);
  225. }
  226. finally
  227. {
  228. persistenceContext.AfterLoad();
  229. }
  230. persistenceContext.InitializeNonLazyCollections();
  231. }
  232. finally
  233. {
  234. persistenceContext.DefaultReadOnly = defaultReadOnlyOrig;
  235. }
  236. return result;
  237. }
  238. /// <summary>
  239. /// Loads a single row from the result set. This is the processing used from the
  240. /// ScrollableResults where no collection fetches were encountered.
  241. /// </summary>
  242. /// <param name="resultSet">The result set from which to do the load.</param>
  243. /// <param name="session">The session from which the request originated.</param>
  244. /// <param name="queryParameters">The query parameters specified by the user.</param>
  245. /// <param name="returnProxies">Should proxies be generated</param>
  246. /// <returns>The loaded "row".</returns>
  247. /// <exception cref="HibernateException" />
  248. protected object LoadSingleRow(IDataReader resultSet, ISessionImplementor session, QueryParameters queryParameters,
  249. bool returnProxies)
  250. {
  251. int entitySpan = EntityPersisters.Length;
  252. IList hydratedObjects = entitySpan == 0 ? null : new List<object>(entitySpan);
  253. object result;
  254. try
  255. {
  256. result =
  257. GetRowFromResultSet(resultSet, session, queryParameters, GetLockModes(queryParameters.LockModes), null,
  258. hydratedObjects, new EntityKey[entitySpan], returnProxies);
  259. }
  260. catch (HibernateException)
  261. {
  262. throw; // Don't call Convert on HibernateExceptions
  263. }
  264. catch (Exception sqle)
  265. {
  266. throw ADOExceptionHelper.Convert(Factory.SQLExceptionConverter, sqle, "could not read next row of results",
  267. SqlString, queryParameters.PositionalParameterValues,
  268. queryParameters.NamedParameters);
  269. }
  270. InitializeEntitiesAndCollections(hydratedObjects, resultSet, session, queryParameters.IsReadOnly(session));
  271. session.PersistenceContext.InitializeNonLazyCollections();
  272. return result;
  273. }
  274. // Not ported: sequentialLoad, loadSequentialRowsForward, loadSequentialRowsReverse
  275. internal static EntityKey GetOptionalObjectKey(QueryParameters queryParameters, ISessionImplementor session)
  276. {
  277. object optionalObject = queryParameters.OptionalObject;
  278. object optionalId = queryParameters.OptionalId;
  279. string optionalEntityName = queryParameters.OptionalEntityName;
  280. if (optionalObject != null && !string.IsNullOrEmpty(optionalEntityName))
  281. {
  282. return new EntityKey(optionalId, session.GetEntityPersister(optionalEntityName, optionalObject), session.EntityMode);
  283. }
  284. else
  285. {
  286. return null;
  287. }
  288. }
  289. internal object GetRowFromResultSet(IDataReader resultSet, ISessionImplementor session,
  290. QueryParameters queryParameters, LockMode[] lockModeArray,
  291. EntityKey optionalObjectKey, IList hydratedObjects, EntityKey[] keys,
  292. bool returnProxies)
  293. {
  294. ILoadable[] persisters = EntityPersisters;
  295. int entitySpan = persisters.Length;
  296. for (int i = 0; i < entitySpan; i++)
  297. {
  298. keys[i] =
  299. GetKeyFromResultSet(i, persisters[i], i == entitySpan - 1 ? queryParameters.OptionalId : null, resultSet, session);
  300. //TODO: the i==entitySpan-1 bit depends upon subclass implementation (very bad)
  301. }
  302. RegisterNonExists(keys, session);
  303. // this call is side-effecty
  304. object[] row =
  305. GetRow(resultSet, persisters, keys, queryParameters.OptionalObject, optionalObjectKey, lockModeArray,
  306. hydratedObjects, session);
  307. ReadCollectionElements(row, resultSet, session);
  308. if (returnProxies)
  309. {
  310. // now get an existing proxy for each row element (if there is one)
  311. for (int i = 0; i < entitySpan; i++)
  312. {
  313. object entity = row[i];
  314. object proxy = session.PersistenceContext.ProxyFor(persisters[i], keys[i], entity);
  315. if (entity != proxy)
  316. {
  317. // Force the proxy to resolve itself
  318. ((INHibernateProxy)proxy).HibernateLazyInitializer.SetImplementation(entity);
  319. row[i] = proxy;
  320. }
  321. }
  322. }
  323. return GetResultColumnOrRow(row, queryParameters.ResultTransformer, resultSet, session);
  324. }
  325. /// <summary>
  326. /// Read any collection elements contained in a single row of the result set
  327. /// </summary>
  328. private void ReadCollectionElements(object[] row, IDataReader resultSet, ISessionImplementor session)
  329. {
  330. //TODO: make this handle multiple collection roles!
  331. ICollectionPersister[] collectionPersisters = CollectionPersisters;
  332. if (collectionPersisters != null)
  333. {
  334. ICollectionAliases[] descriptors = CollectionAliases;
  335. int[] collectionOwners = CollectionOwners;
  336. for (int i = 0; i < collectionPersisters.Length; i++)
  337. {
  338. bool hasCollectionOwners = collectionOwners != null && collectionOwners[i] > -1;
  339. //true if this is a query and we are loading multiple instances of the same collection role
  340. //otherwise this is a CollectionInitializer and we are loading up a single collection or batch
  341. object owner = hasCollectionOwners ? row[collectionOwners[i]] : null;
  342. //if null, owner will be retrieved from session
  343. ICollectionPersister collectionPersister = collectionPersisters[i];
  344. object key;
  345. if (owner == null)
  346. {
  347. key = null;
  348. }
  349. else
  350. {
  351. key = collectionPersister.CollectionType.GetKeyOfOwner(owner, session);
  352. //TODO: old version did not require hashmap lookup:
  353. //keys[collectionOwner].getIdentifier()
  354. }
  355. ReadCollectionElement(owner, key, collectionPersister, descriptors[i], resultSet, session);
  356. }
  357. }
  358. }
  359. private IList DoQuery(ISessionImplementor session, QueryParameters queryParameters, bool returnProxies)
  360. {
  361. RowSelection selection = queryParameters.RowSelection;
  362. int maxRows = HasMaxRows(selection) ? selection.MaxRows : int.MaxValue;
  363. int entitySpan = EntityPersisters.Length;
  364. List<object> hydratedObjects = entitySpan == 0 ? null : new List<object>(entitySpan * 10);
  365. IDbCommand st = PrepareQueryCommand(queryParameters, false, session);
  366. IDataReader rs = GetResultSet(st, queryParameters.HasAutoDiscoverScalarTypes, queryParameters.Callable, selection,
  367. session);
  368. // would be great to move all this below here into another method that could also be used
  369. // from the new scrolling stuff.
  370. //
  371. // Would need to change the way the max-row stuff is handled (i.e. behind an interface) so
  372. // that I could do the control breaking at the means to know when to stop
  373. LockMode[] lockModeArray = GetLockModes(queryParameters.LockModes);
  374. EntityKey optionalObjectKey = GetOptionalObjectKey(queryParameters, session);
  375. bool createSubselects = IsSubselectLoadingEnabled;
  376. List<EntityKey[]> subselectResultKeys = createSubselects ? new List<EntityKey[]>() : null;
  377. IList results = new List<object>();
  378. try
  379. {
  380. HandleEmptyCollections(queryParameters.CollectionKeys, rs, session);
  381. EntityKey[] keys = new EntityKey[entitySpan]; // we can reuse it each time
  382. if (Log.IsDebugEnabled)
  383. {
  384. Log.Debug("processing result set");
  385. }
  386. int count;
  387. for (count = 0; count < maxRows && rs.Read(); count++)
  388. {
  389. if (Log.IsDebugEnabled)
  390. {
  391. Log.Debug("result set row: " + count);
  392. }
  393. object result = GetRowFromResultSet(rs, session, queryParameters, lockModeArray, optionalObjectKey, hydratedObjects,
  394. keys, returnProxies);
  395. results.Add(result);
  396. if (createSubselects)
  397. {
  398. subselectResultKeys.Add(keys);
  399. keys = new EntityKey[entitySpan]; //can't reuse in this case
  400. }
  401. }
  402. if (Log.IsDebugEnabled)
  403. {
  404. Log.Debug(string.Format("done processing result set ({0} rows)", count));
  405. }
  406. }
  407. catch (Exception e)
  408. {
  409. e.Data["actual-sql-query"] = st.CommandText;
  410. throw;
  411. }
  412. finally
  413. {
  414. session.Batcher.CloseCommand(st, rs);
  415. }
  416. InitializeEntitiesAndCollections(hydratedObjects, rs, session, queryParameters.IsReadOnly(session));
  417. if (createSubselects)
  418. {
  419. CreateSubselects(subselectResultKeys, queryParameters, session);
  420. }
  421. return results;
  422. }
  423. protected bool HasSubselectLoadableCollections()
  424. {
  425. foreach (ILoadable loadable in EntityPersisters)
  426. {
  427. if (loadable.HasSubselectLoadableCollections)
  428. {
  429. return true;
  430. }
  431. }
  432. return false;
  433. }
  434. private static ISet<EntityKey>[] Transpose(IList<EntityKey[]> keys)
  435. {
  436. ISet<EntityKey>[] result = new ISet<EntityKey>[keys[0].Length];
  437. for (int j = 0; j < result.Length; j++)
  438. {
  439. result[j] = new HashedSet<EntityKey>();
  440. for (int i = 0; i < keys.Count; i++)
  441. {
  442. EntityKey key = keys[i][j];
  443. if (key != null)
  444. {
  445. result[j].Add(key);
  446. }
  447. }
  448. }
  449. return result;
  450. }
  451. internal void CreateSubselects(IList<EntityKey[]> keys, QueryParameters queryParameters, ISessionImplementor session)
  452. {
  453. if (keys.Count > 1)
  454. {
  455. //if we only returned one entity, query by key is more efficient
  456. var subSelects = CreateSubselects(keys, queryParameters).ToArray();
  457. foreach (EntityKey[] rowKeys in keys)
  458. {
  459. for (int i = 0; i < rowKeys.Length; i++)
  460. {
  461. if (rowKeys[i] != null && subSelects[i] != null)
  462. {
  463. session.PersistenceContext.BatchFetchQueue.AddSubselect(rowKeys[i], subSelects[i]);
  464. }
  465. }
  466. }
  467. }
  468. }
  469. private IEnumerable<SubselectFetch> CreateSubselects(IList<EntityKey[]> keys, QueryParameters queryParameters)
  470. {
  471. // see NH-2123 NH-2125
  472. ISet<EntityKey>[] keySets = Transpose(keys);
  473. ILoadable[] loadables = EntityPersisters;
  474. string[] aliases = Aliases;
  475. for (int i = 0; i < loadables.Length; i++)
  476. {
  477. if (loadables[i].HasSubselectLoadableCollections)
  478. {
  479. yield return new SubselectFetch(aliases[i], loadables[i], queryParameters, keySets[i]);
  480. }
  481. else
  482. {
  483. yield return null;
  484. }
  485. }
  486. }
  487. internal void InitializeEntitiesAndCollections(IList hydratedObjects, object resultSetId, ISessionImplementor session, bool readOnly)
  488. {
  489. ICollectionPersister[] collectionPersisters = CollectionPersisters;
  490. if (collectionPersisters != null)
  491. {
  492. for (int i = 0; i < collectionPersisters.Length; i++)
  493. {
  494. if (collectionPersisters[i].IsArray)
  495. {
  496. //for arrays, we should end the collection load before resolving
  497. //the entities, since the actual array instances are not instantiated
  498. //during loading
  499. //TODO: or we could do this polymorphically, and have two
  500. // different operations implemented differently for arrays
  501. EndCollectionLoad(resultSetId, session, collectionPersisters[i]);
  502. }
  503. }
  504. }
  505. //important: reuse the same event instances for performance!
  506. PreLoadEvent pre;
  507. PostLoadEvent post;
  508. if (session.IsEventSource)
  509. {
  510. var eventSourceSession = (IEventSource)session;
  511. pre = new PreLoadEvent(eventSourceSession);
  512. post = new PostLoadEvent(eventSourceSession);
  513. }
  514. else
  515. {
  516. pre = null;
  517. post = null;
  518. }
  519. if (hydratedObjects != null)
  520. {
  521. int hydratedObjectsSize = hydratedObjects.Count;
  522. if (Log.IsDebugEnabled)
  523. {
  524. Log.Debug(string.Format("total objects hydrated: {0}", hydratedObjectsSize));
  525. }
  526. for (int i = 0; i < hydratedObjectsSize; i++)
  527. {
  528. TwoPhaseLoad.InitializeEntity(hydratedObjects[i], readOnly, session, pre, post);
  529. }
  530. }
  531. if (collectionPersisters != null)
  532. {
  533. for (int i = 0; i < collectionPersisters.Length; i++)
  534. {
  535. if (!collectionPersisters[i].IsArray)
  536. {
  537. //for sets, we should end the collection load after resolving
  538. //the entities, since we might call hashCode() on the elements
  539. //TODO: or we could do this polymorphically, and have two
  540. // different operations implemented differently for arrays
  541. EndCollectionLoad(resultSetId, session, collectionPersisters[i]);
  542. }
  543. }
  544. }
  545. }
  546. private static void EndCollectionLoad(object resultSetId, ISessionImplementor session, ICollectionPersister collectionPersister)
  547. {
  548. //this is a query and we are loading multiple instances of the same collection role
  549. session.PersistenceContext.LoadContexts.GetCollectionLoadContext((IDataReader)resultSetId).EndLoadingCollections(
  550. collectionPersister);
  551. }
  552. public virtual IList GetResultList(IList results, IResultTransformer resultTransformer)
  553. {
  554. return results;
  555. }
  556. /// <summary>
  557. /// Get the actual object that is returned in the user-visible result list.
  558. /// </summary>
  559. /// <remarks>
  560. /// This empty implementation merely returns its first argument. This is
  561. /// overridden by some subclasses.
  562. /// </remarks>
  563. protected virtual object GetResultColumnOrRow(object[] row, IResultTransformer resultTransformer, IDataReader rs, ISessionImplementor session)
  564. {
  565. return row;
  566. }
  567. /// <summary>
  568. /// For missing objects associated by one-to-one with another object in the
  569. /// result set, register the fact that the the object is missing with the
  570. /// session.
  571. /// </summary>
  572. private void RegisterNonExists(EntityKey[] keys, ISessionImplementor session)
  573. {
  574. int[] owners = Owners;
  575. if (owners != null)
  576. {
  577. EntityType[] ownerAssociationTypes = OwnerAssociationTypes;
  578. for (int i = 0; i < keys.Length; i++)
  579. {
  580. int owner = owners[i];
  581. if (owner > -1)
  582. {
  583. EntityKey ownerKey = keys[owner];
  584. if (keys[i] == null && ownerKey != null)
  585. {
  586. bool isOneToOneAssociation = ownerAssociationTypes != null && ownerAssociationTypes[i] != null
  587. && ownerAssociationTypes[i].IsOneToOne;
  588. if (isOneToOneAssociation)
  589. {
  590. session.PersistenceContext.AddNullProperty(ownerKey, ownerAssociationTypes[i].PropertyName);
  591. }
  592. }
  593. }
  594. }
  595. }
  596. }
  597. /// <summary>
  598. /// Read one collection element from the current row of the ADO.NET result set
  599. /// </summary>
  600. private static void ReadCollectionElement(object optionalOwner, object optionalKey, ICollectionPersister persister,
  601. ICollectionAliases descriptor, IDataReader rs, ISessionImplementor session)
  602. {
  603. IPersistenceContext persistenceContext = session.PersistenceContext;
  604. object collectionRowKey = persister.ReadKey(rs, descriptor.SuffixedKeyAliases, session);
  605. if (collectionRowKey != null)
  606. {
  607. // we found a collection element in the result set
  608. if (Log.IsDebugEnabled)
  609. {
  610. Log.Debug("found row of collection: " + MessageHelper.InfoString(persister, collectionRowKey));
  611. }
  612. object owner = optionalOwner;
  613. if (owner == null)
  614. {
  615. owner = persistenceContext.GetCollectionOwner(collectionRowKey, persister);
  616. if (owner == null)
  617. {
  618. //TODO: This is assertion is disabled because there is a bug that means the
  619. // original owner of a transient, uninitialized collection is not known
  620. // if the collection is re-referenced by a different object associated
  621. // with the current Session
  622. //throw new AssertionFailure("bug loading unowned collection");
  623. }
  624. }
  625. IPersistentCollection rowCollection =
  626. persistenceContext.LoadContexts.GetCollectionLoadContext(rs).GetLoadingCollection(persister, collectionRowKey);
  627. if (rowCollection != null)
  628. {
  629. rowCollection.ReadFrom(rs, persister, descriptor, owner);
  630. }
  631. }
  632. else if (optionalKey != null)
  633. {
  634. // we did not find a collection element in the result set, so we
  635. // ensure that a collection is created with the owner's identifier,
  636. // since what we have is an empty collection
  637. if (Log.IsDebugEnabled)
  638. {
  639. Log.Debug("result set contains (possibly empty) collection: " + MessageHelper.InfoString(persister, optionalKey));
  640. }
  641. persistenceContext.LoadContexts.GetCollectionLoadContext(rs).GetLoadingCollection(persister, optionalKey);
  642. // handle empty collection
  643. }
  644. // else no collection element, but also no owner
  645. }
  646. /// <summary>
  647. /// If this is a collection initializer, we need to tell the session that a collection
  648. /// is being initilized, to account for the possibility of the collection having
  649. /// no elements (hence no rows in the result set).
  650. /// </summary>
  651. internal void HandleEmptyCollections(object[] keys, object resultSetId, ISessionImplementor session)
  652. {
  653. if (keys != null)
  654. {
  655. // this is a collection initializer, so we must create a collection
  656. // for each of the passed-in keys, to account for the possibility
  657. // that the collection is empty and has no rows in the result set
  658. ICollectionPersister[] collectionPersisters = CollectionPersisters;
  659. for (int j = 0; j < collectionPersisters.Length; j++)
  660. {
  661. for (int i = 0; i < keys.Length; i++)
  662. {
  663. // handle empty collections
  664. if (Log.IsDebugEnabled)
  665. {
  666. Log.Debug("result set contains (possibly empty) collection: "
  667. + MessageHelper.InfoString(collectionPersisters[j], keys[i]));
  668. }
  669. session.PersistenceContext.LoadContexts.GetCollectionLoadContext((IDataReader)resultSetId).GetLoadingCollection(
  670. collectionPersisters[j], keys[i]);
  671. }
  672. }
  673. }
  674. // else this is not a collection initializer (and empty collections will
  675. // be detected by looking for the owner's identifier in the result set)
  676. }
  677. /// <summary>
  678. /// Read a row of <c>EntityKey</c>s from the <c>IDataReader</c> into the given array.
  679. /// </summary>
  680. /// <remarks>
  681. /// Warning: this method is side-effecty. If an <c>id</c> is given, don't bother going
  682. /// to the <c>IDataReader</c>
  683. /// </remarks>
  684. private EntityKey GetKeyFromResultSet(int i, IEntityPersister persister, object id, IDataReader rs, ISessionImplementor session)
  685. {
  686. object resultId;
  687. // if we know there is exactly 1 row, we can skip.
  688. // it would be great if we could _always_ skip this;
  689. // it is a problem for <key-many-to-one>
  690. if (IsSingleRowLoader && id != null)
  691. {
  692. resultId = id;
  693. }
  694. else
  695. {
  696. IType idType = persister.IdentifierType;
  697. resultId = idType.NullSafeGet(rs, EntityAliases[i].SuffixedKeyAliases, session, null);
  698. bool idIsResultId = id != null && resultId != null && idType.IsEqual(id, resultId, session.EntityMode, _factory);
  699. if (idIsResultId)
  700. {
  701. resultId = id; //use the id passed in
  702. }
  703. }
  704. return resultId == null ? null : new EntityKey(resultId, persister, session.EntityMode);
  705. }
  706. /// <summary>
  707. /// Check the version of the object in the <c>IDataReader</c> against
  708. /// the object version in the session cache, throwing an exception
  709. /// if the version numbers are different.
  710. /// </summary>
  711. /// <exception cref="StaleObjectStateException"></exception>
  712. private void CheckVersion(int i, IEntityPersister persister, object id, object entity, IDataReader rs, ISessionImplementor session)
  713. {
  714. object version = session.PersistenceContext.GetEntry(entity).Version;
  715. // null version means the object is in the process of being loaded somewhere else in the ResultSet
  716. if (version != null)
  717. {
  718. IVersionType versionType = persister.VersionType;
  719. object currentVersion = versionType.NullSafeGet(rs, EntityAliases[i].SuffixedVersionAliases, session, null);
  720. if (!versionType.IsEqual(version, currentVersion))
  721. {
  722. if (session.Factory.Statistics.IsStatisticsEnabled)
  723. {
  724. session.Factory.StatisticsImplementor.OptimisticFailure(persister.EntityName);
  725. }
  726. throw new StaleObjectStateException(persister.EntityName, id);
  727. }
  728. }
  729. }
  730. /// <summary>
  731. /// Resolve any ids for currently loaded objects, duplications within the <c>IDataReader</c>,
  732. /// etc. Instanciate empty objects to be initialized from the <c>IDataReader</c>. Return an
  733. /// array of objects (a row of results) and an array of booleans (by side-effect) that determine
  734. /// wheter the corresponding object should be initialized
  735. /// </summary>
  736. private object[] GetRow(IDataReader rs, ILoadable[] persisters, EntityKey[] keys, object optionalObject,
  737. EntityKey optionalObjectKey, LockMode[] lockModes, IList hydratedObjects,
  738. ISessionImplementor session)
  739. {
  740. int cols = persisters.Length;
  741. IEntityAliases[] descriptors = EntityAliases;
  742. if (Log.IsDebugEnabled)
  743. {
  744. Log.Debug("result row: " + StringHelper.ToString(keys));
  745. }
  746. object[] rowResults = new object[cols];
  747. for (int i = 0; i < cols; i++)
  748. {
  749. object obj = null;
  750. EntityKey key = keys[i];
  751. if (keys[i] == null)
  752. {
  753. // do nothing
  754. /* TODO NH-1001 : if (persisters[i]...EntityType) is an OneToMany or a ManyToOne and
  755. * the keys.length > 1 and the relation IsIgnoreNotFound probably we are in presence of
  756. * an load with "outer join" the relation can be considerer loaded even if the key is null (mean not found)
  757. */
  758. }
  759. else
  760. {
  761. //If the object is already loaded, return the loaded one
  762. obj = session.GetEntityUsingInterceptor(key);
  763. if (obj != null)
  764. {
  765. //its already loaded so dont need to hydrate it
  766. InstanceAlreadyLoaded(rs, i, persisters[i], key, obj, lockModes[i], session);
  767. }
  768. else
  769. {
  770. obj =
  771. InstanceNotYetLoaded(rs, i, persisters[i], key, lockModes[i], descriptors[i].RowIdAlias, optionalObjectKey,
  772. optionalObject, hydratedObjects, session);
  773. }
  774. }
  775. rowResults[i] = obj;
  776. }
  777. return rowResults;
  778. }
  779. /// <summary>
  780. /// The entity instance is already in the session cache
  781. /// </summary>
  782. private void InstanceAlreadyLoaded(IDataReader rs, int i, IEntityPersister persister, EntityKey key, object obj,
  783. LockMode lockMode, ISessionImplementor session)
  784. {
  785. if (!persister.IsInstance(obj, session.EntityMode))
  786. {
  787. string errorMsg = string.Format("loading object was of wrong class [{0}]", obj.GetType().FullName);
  788. throw new WrongClassException(errorMsg, key.Identifier, persister.EntityName);
  789. }
  790. if (LockMode.None != lockMode && UpgradeLocks())
  791. {
  792. EntityEntry entry = session.PersistenceContext.GetEntry(obj);
  793. bool isVersionCheckNeeded = persister.IsVersioned && entry.LockMode.LessThan(lockMode);
  794. // we don't need to worry about existing version being uninitialized
  795. // because this block isn't called by a re-entrant load (re-entrant
  796. // load _always_ have lock mode NONE
  797. if (isVersionCheckNeeded)
  798. {
  799. // we only check the version when _upgrading_ lock modes
  800. CheckVersion(i, persister, key.Identifier, obj, rs, session);
  801. // we need to upgrade the lock mode to the mode requested
  802. entry.LockMode = lockMode;
  803. }
  804. }
  805. }
  806. /// <summary>
  807. /// The entity instance is not in the session cache
  808. /// </summary>
  809. private object InstanceNotYetLoaded(IDataReader dr, int i, ILoadable persister, EntityKey key, LockMode lockMode,
  810. string rowIdAlias, EntityKey optionalObjectKey, object optionalObject,
  811. IList hydratedObjects, ISessionImplementor session)
  812. {
  813. object obj;
  814. string instanceClass = GetInstanceClass(dr, i, persister, key.Identifier, session);
  815. if (optionalObjectKey != null && key.Equals(optionalObjectKey))
  816. {
  817. // its the given optional object
  818. obj = optionalObject;
  819. }
  820. else
  821. {
  822. obj = session.Instantiate(instanceClass, key.Identifier);
  823. }
  824. // need to hydrate it
  825. // grab its state from the DataReader and keep it in the Session
  826. // (but don't yet initialize the object itself)
  827. // note that we acquired LockMode.READ even if it was not requested
  828. LockMode acquiredLockMode = lockMode == LockMode.None ? LockMode.Read : lockMode;
  829. LoadFromResultSet(dr, i, obj, instanceClass, key, rowIdAlias, acquiredLockMode, persister, session);
  830. // materialize associations (and initialize the object) later
  831. hydratedObjects.Add(obj);
  832. return obj;
  833. }
  834. private bool IsEagerPropertyFetchEnabled(int i)
  835. {
  836. bool[] array = EntityEagerPropertyFetches;
  837. return array != null && array[i];
  838. }
  839. /// <summary>
  840. /// Hydrate the state of an object from the SQL <c>IDataReader</c>, into
  841. /// an array of "hydrated" values (do not resolve associations yet),
  842. /// and pass the hydrated state to the session.
  843. /// </summary>
  844. private void LoadFromResultSet(IDataReader rs, int i, object obj, string instanceClass, EntityKey key,
  845. string rowIdAlias, LockMode lockMode, ILoadable rootPersister,
  846. ISessionImplementor session)
  847. {
  848. object id = key.Identifier;
  849. // Get the persister for the _subclass_
  850. ILoadable persister = (ILoadable)Factory.GetEntityPersister(instanceClass);
  851. if (Log.IsDebugEnabled)
  852. {
  853. Log.Debug("Initializing object from DataReader: " + MessageHelper.InfoString(persister, id));
  854. }
  855. bool eagerPropertyFetch = IsEagerPropertyFetchEnabled(i);
  856. // add temp entry so that the next step is circular-reference
  857. // safe - only needed because some types don't take proper
  858. // advantage of two-phase-load (esp. components)
  859. TwoPhaseLoad.AddUninitializedEntity(key, obj, persister, lockMode, !eagerPropertyFetch, session);
  860. // This is not very nice (and quite slow):
  861. string[][] cols = persister == rootPersister
  862. ? EntityAliases[i].SuffixedPropertyAliases
  863. : EntityAliases[i].GetSuffixedPropertyAliases(persister);
  864. object[] values = persister.Hydrate(rs, id, obj, rootPersister, cols, eagerPropertyFetch, session);
  865. object rowId = persister.HasRowId ? rs[rowIdAlias] : null;
  866. IAssociationType[] ownerAssociationTypes = OwnerAssociationTypes;
  867. if (ownerAssociationTypes != null && ownerAssociationTypes[i] != null)
  868. {
  869. string ukName = ownerAssociationTypes[i].RHSUniqueKeyPropertyName;
  870. if (ukName != null)
  871. {
  872. int index = ((IUniqueKeyLoadable)persister).GetPropertyIndex(ukName);
  873. IType type = persister.PropertyTypes[index];
  874. // polymorphism not really handled completely correctly,
  875. // perhaps...well, actually its ok, assuming that the
  876. // entity name used in the lookup is the same as the
  877. // the one used here, which it will be
  878. EntityUniqueKey euk =
  879. new EntityUniqueKey(rootPersister.EntityName, ukName, type.SemiResolve(values[index], session, obj), type,
  880. session.EntityMode, session.Factory);
  881. session.PersistenceContext.AddEntity(euk, obj);
  882. }
  883. }
  884. TwoPhaseLoad.PostHydrate(persister, id, values, rowId, obj, lockMode, !eagerPropertyFetch, session);
  885. }
  886. /// <summary>
  887. /// Determine the concrete class of an instance for the <c>IDataReader</c>
  888. /// </summary>
  889. private string GetInstanceClass(IDataReader rs, int i, ILoadable persister, object id, ISessionImplementor session)
  890. {
  891. if (persister.HasSubclasses)
  892. {
  893. // code to handle subclasses of topClass
  894. object discriminatorValue =
  895. persister.DiscriminatorType.NullSafeGet(rs, EntityAliases[i].SuffixedDiscriminatorAlias, session, null);
  896. string result = persister.GetSubclassForDiscriminatorValue(discriminatorValue);
  897. if (result == null)
  898. {
  899. // woops we got an instance of another class hierarchy branch.
  900. throw new WrongClassException(string.Format("Discriminator was: '{0}'", discriminatorValue), id,
  901. persister.EntityName);
  902. }
  903. return result;
  904. }
  905. return persister.EntityName;
  906. }
  907. /// <summary>
  908. /// Advance the cursor to the first required row of the <c>IDataReader</c>
  909. /// </summary>
  910. internal static void Advance(IDataReader rs, RowSelection selection)
  911. {
  912. int firstRow = GetFirstRow(selection);
  913. if (firstRow != 0)
  914. {
  915. // DataReaders are forward-only, readonly, so we have to step through
  916. for (int i = 0; i < firstRow; i++)
  917. {
  918. rs.Read();
  919. }
  920. }
  921. }
  922. internal static bool HasMaxRows(RowSelection selection)
  923. {
  924. // it used to be selection.MaxRows != null -> since an Int32 will always
  925. // have a value I'll compare it to the static field NoValue used to initialize
  926. // max rows to nothing
  927. return selection != null && selection.MaxRows != RowSelection.NoValue;
  928. }
  929. private static bool HasOffset(RowSelection selection)
  930. {
  931. return selection != null && selection.FirstRow != RowSelection.NoValue;
  932. }
  933. internal static int GetFirstRow(RowSelection selection)
  934. {
  935. if (selection == null || !selection.DefinesLimits)
  936. {
  937. return 0;
  938. }
  939. return selection.FirstRow > 0 ? selection.FirstRow : 0;
  940. }
  941. /// <summary>
  942. /// Should we pre-process the SQL string, adding a dialect-specific
  943. /// LIMIT clause.
  944. /// </summary>
  945. /// <param name="selection"></param>
  946. /// <param name="dialect"></param>
  947. /// <returns></returns>
  948. internal bool UseLimit(RowSelection selection, Dialect.Dialect dialect)
  949. {
  950. return dialect.SupportsLimit && (HasMaxRows(selection) || HasOffset(selection));
  951. }
  952. /// <summary>
  953. /// Performs dialect-specific manipulations on the offset value before returning it.
  954. /// This method is applicable for use in limit statements only.
  955. /// </summary>
  956. internal static int? GetOffsetUsingDialect(RowSelection selection, Dialect.Dialect dialect)
  957. {
  958. int firstRow = GetFirstRow(selection);
  959. if (firstRow == 0)
  960. return null;
  961. return dialect.GetOffsetValue(firstRow);
  962. }
  963. /// <summary>
  964. /// Performs dialect-specific manipulations on the limit value before returning it.
  965. /// This method is applicable for use in limit statements only.
  966. /// </summary>
  967. internal static int? GetLimitUsingDialect(RowSelection selection, Dialect.Dialect dialect)
  968. {
  969. if (selection == null || selection.MaxRows == RowSelection.NoValue)
  970. return null;
  971. return dialect.GetLimitValue(GetFirstRow(selection), selection.MaxRows);
  972. }
  973. /// <summary>
  974. /// Obtain an <c>IDbCommand</c> with all parameters pre-bound. Bind positional parameters,
  975. /// named parameters, and limit parameters.
  976. /// </summary>
  977. /// <remarks>
  978. /// Creates an IDbCommand object and populates it with the values necessary to execute it against the
  979. /// database to Load an Entity.
  980. /// </remarks>
  981. /// <param name="queryParameters">The <see cref="QueryParameters"/> to use for the IDbCommand.</param>
  982. /// <param name="scroll">TODO: find out where this is used...</param>
  983. /// <param name="session">The SessionImpl this Command is being prepared in.</param>
  984. /// <returns>A CommandWrapper wrapping an IDbCommand that is ready to be executed.</returns>
  985. protected internal virtual IDbCommand PrepareQueryCommand(QueryParameters queryParameters, bool scroll, ISessionImplementor session)
  986. {
  987. ISqlCommand sqlCommand = CreateSqlCommand(queryParameters, session);
  988. SqlString sqlString = sqlCommand.Query;
  989. sqlCommand.ResetParametersIndexesForTheCommand(0);
  990. IDbCommand command = session.Batcher.PrepareQueryCommand(CommandType.Text, sqlString, sqlCommand.ParameterTypes);
  991. try
  992. {
  993. RowSelection selection = queryParameters.RowSelection;
  994. if (selection != null && selection.Timeout != RowSelection.NoValue)
  995. {
  996. command.CommandTimeout = selection.Timeout;
  997. }
  998. sqlCommand.Bind(command, session);
  999. IDriver driver = _factory.ConnectionProvider.Driver;
  1000. driver.RemoveUnusedCommandParameters(command, sqlString);
  1001. driver.ExpandQueryParameters(command, sqlString);
  1002. }
  1003. catch (HibernateException)
  1004. {
  1005. session.Batcher.CloseCommand(command, null);
  1006. throw;
  1007. }
  1008. catch (Exception sqle)
  1009. {
  1010. session.Batcher.CloseCommand(command, null);
  1011. ADOExceptionReporter.LogExceptions(sqle);
  1012. throw;
  1013. }
  1014. return command;
  1015. }
  1016. /// <summary>
  1017. /// Some dialect-specific LIMIT clauses require the maximium last row number
  1018. /// (aka, first_row_number + total_row_count), while others require the maximum
  1019. /// returned row count (the total maximum number of rows to return).
  1020. /// </summary>
  1021. /// <param name="selection">The selection criteria </param>
  1022. /// <param name="dialect">The dialect </param>
  1023. /// <returns> The appropriate value to bind into the limit clause. </returns>
  1024. internal static int GetMaxOrLimit(Dialect.Dialect dialect, RowSelection selection)
  1025. {
  1026. int firstRow = GetFirstRow(selection);
  1027. int rowCount = selection.MaxRows;
  1028. if (rowCount == RowSelection.NoValue)
  1029. return int.MaxValue;
  1030. return dialect.GetLimitValue(firstRow, rowCount);
  1031. }
  1032. /// <summary>
  1033. /// Fetch a <c>IDbCommand</c>, call <c>SetMaxRows</c> and then execute it,
  1034. /// advance to the first result and return an SQL <c>IDataReader</c>
  1035. /// </summary>
  1036. /// <param name="st">The <see cref="IDbCommand" /> to execute.</param>
  1037. /// <param name="selection">The <see cref="RowSelection"/> to apply to the <see cref="IDbCommand"/> and <see cref="IDataReader"/>.</param>
  1038. /// <param name="autoDiscoverTypes">true if result types need to be auto-discovered by the loader; false otherwise.</param>
  1039. /// <param name="session">The <see cref="ISession" /> to load in.</param>
  1040. /// <param name="callable"></param>
  1041. /// <returns>An IDataReader advanced to the first record in RowSelection.</returns>
  1042. protected IDataReader GetResultSet(IDbCommand st, bool autoDiscoverTypes, bool callable, RowSelection selection, ISessionImplementor session)
  1043. {
  1044. IDataReader rs = null;
  1045. try
  1046. {
  1047. Log.Info(st.CommandText);
  1048. // TODO NH: Callable
  1049. rs = session.Batcher.ExecuteReader(st);
  1050. //NH: this is checked outside the WrapResultSet because we
  1051. // want to avoid the syncronization overhead in the vast majority
  1052. // of cases where IsWrapResultSetsEnabled is set to false
  1053. if (session.Factory.Settings.IsWrapResultSetsEnabled)
  1054. rs = WrapResultSet(rs);
  1055. Dialect.Dialect dialect = session.Factory.Dialect;
  1056. if (!dialect.SupportsLimitOffset || !UseLimit(selection, dialect))
  1057. {
  1058. Advance(rs, selection);
  1059. }
  1060. if (autoDiscoverTypes)
  1061. {
  1062. AutoDiscoverTypes(rs);
  1063. }
  1064. return rs;
  1065. }
  1066. catch (Exception sqle)
  1067. {
  1068. ADOExceptionReporter.LogExceptions(sqle);
  1069. session.Batcher.CloseCommand(st, rs);
  1070. throw;
  1071. }
  1072. }
  1073. protected virtual void AutoDiscoverTypes(IDataReader rs)
  1074. {
  1075. throw new AssertionFailure("Auto discover types not supported in this loader");
  1076. }
  1077. [MethodImpl(MethodImplOptions.Synchronized)]
  1078. private IDataReader WrapResultSet(IDataReader rs)
  1079. {
  1080. // synchronized to avoid multi-thread access issues; defined as method synch to avoid
  1081. // potential deadlock issues due to nature of code.
  1082. try
  1083. {
  1084. Log.Debug("Wrapping result set [" + rs + "]");
  1085. return new ResultSetWrapper(rs, RetreiveColumnNameToIndexCache(rs));
  1086. }
  1087. catch (Exception e)
  1088. {
  1089. Log.Info("Error wrapping result set", e);
  1090. return rs;
  1091. }
  1092. }
  1093. private ColumnNameCache RetreiveColumnNameToIndexCache(IDataReader rs)
  1094. {
  1095. if (_columnNameCache == null)
  1096. {
  1097. Log.Debug("Building columnName->columnIndex cache");
  1098. _columnNameCache = new ColumnNameCache(rs.GetSchemaTable().Rows.Count);
  1099. }
  1100. return _columnNameCache;
  1101. }
  1102. /// <summary>
  1103. /// Called by subclasses that load entities
  1104. /// </summary>
  1105. protected IList LoadEntity(ISessionImplementor session, object id, IType identifierType, object optionalObject,
  1106. string optionalEntityName, object optionalIdentifier, IEntityPersister persister)
  1107. {
  1108. if (Log.IsDebugEnabled)
  1109. {
  1110. Log.Debug("loading entity: " + MessageHelper.InfoString(persister, id, identifierType, Factory));
  1111. }
  1112. IList result;
  1113. try
  1114. {
  1115. QueryParameters qp =
  1116. new QueryParameters(new IType[] { identifierType }, new object[] { id }, optionalObject, optionalEntityName,
  1117. optionalIdentifier);
  1118. result = DoQueryAndInitializeNonLazyCollections(session, qp, false);
  1119. }
  1120. catch (HibernateException)
  1121. {
  1122. throw;
  1123. }
  1124. catch (Exception sqle)
  1125. {
  1126. ILoadable[] persisters = EntityPersisters;
  1127. throw ADOExceptionHelper.Convert(Factory.SQLExceptionConverter, sqle,
  1128. "could not load an entity: "
  1129. +
  1130. MessageHelper.InfoString(persisters[persisters.Length - 1], id, identifierType,
  1131. Factory), SqlString);
  1132. }
  1133. Log.Debug("done entity load");
  1134. return result;
  1135. }
  1136. protected IList LoadEntity(ISessionImplementor session, object key, object index, IType keyType, IType indexType,
  1137. IEntityPersister persister)
  1138. {
  1139. Log.Debug("loading collection element by index");
  1140. IList result;
  1141. try
  1142. {
  1143. result =
  1144. DoQueryAndInitializeNonLazyCollections(session,
  1145. new QueryParameters(new IType[] { keyType, indexType },
  1146. new object[] { key, index }), false);
  1147. }
  1148. catch (Exception sqle)
  1149. {
  1150. throw ADOExceptionHelper.Convert(_factory.SQLExceptionConverter, sqle, "could not collection element by index",
  1151. SqlString);
  1152. }
  1153. Log.Debug("done entity load");
  1154. return result;
  1155. }
  1156. /// <summary>
  1157. /// Called by subclasses that batch load entities
  1158. /// </summary>
  1159. protected internal IList LoadEntityBatch(ISessionImplementor session, object[] ids, IType idType,
  1160. object optionalObject, string optionalEntityName, object optionalId,
  1161. IEntityPersister persister)
  1162. {
  1163. if (Log.IsDebugEnabled)
  1164. {
  1165. Log.Debug("batch loading entity: " + MessageHelper.InfoString(persister, ids, Factory));
  1166. }
  1167. IType[] types = new IType[ids.Length];
  1168. ArrayHelper.Fill(types, idType);
  1169. IList result;
  1170. try
  1171. {
  1172. result =
  1173. DoQueryAndInitializeNonLazyCollections(session,
  1174. new QueryParameters(types, ids, optionalObject, optionalEntityName,
  1175. optionalId), false);
  1176. }
  1177. catch (HibernateException)
  1178. {
  1179. throw;
  1180. }
  1181. catch (Exception sqle)
  1182. {
  1183. throw ADOExceptionHelper.Convert(Factory.SQLExceptionConverter, sqle,
  1184. "could not load an entity batch: "
  1185. + MessageHelper.InfoString(persister, ids, Factory), SqlString);
  1186. // NH: Hibernate3 passes EntityPersisters[0] instead of persister, I think it's wrong.
  1187. }
  1188. Log.Debug("done entity batch load");
  1189. return result;
  1190. }
  1191. /// <summary>
  1192. /// Called by subclasses that load collections
  1193. /// </summary>
  1194. public void LoadCollection(ISessionImplementor session, object id, IType type)
  1195. {
  1196. if (Log.IsDebugEnabled)
  1197. {
  1198. Log.Debug("loading collection: " + MessageHelper.InfoString(CollectionPersisters[0], id));
  1199. }
  1200. object[] ids = new object[] { id };
  1201. try
  1202. {
  1203. DoQueryAndInitializeNonLazyCollections(session, new QueryParameters(new IType[] { type }, ids, ids), true);
  1204. }
  1205. catch (HibernateException)
  1206. {
  1207. // Do not call Convert on HibernateExceptions
  1208. throw;
  1209. }
  1210. catch (Exception sqle)
  1211. {
  1212. throw ADOExceptionHelper.Convert(Factory.SQLExceptionConverter, sqle,
  1213. "could not initialize a collection: "
  1214. + MessageHelper.InfoString(CollectionPersisters[0], id), SqlString);
  1215. }
  1216. Log.Debug("done loading collection");
  1217. }
  1218. /// <summary>
  1219. /// Called by wrappers that batch initialize collections
  1220. /// </summary>
  1221. public void LoadCollectionBatch(ISessionImplementor session, object[] ids, IType type)
  1222. {
  1223. if (Log.IsDebugEnabled)
  1224. {
  1225. Log.Debug("batch loading collection: " + MessageHelper.InfoString(CollectionPersisters[0], ids));
  1226. }
  1227. IType[] idTypes = new IType[ids.Length];
  1228. ArrayHelper.Fill(idTypes, type);
  1229. try
  1230. {
  1231. DoQueryAndInitializeNonLazyCollections(session, new QueryParameters(idTypes, ids, ids), true);
  1232. }
  1233. catch (HibernateException)
  1234. {
  1235. // Do not call Convert on HibernateExceptions
  1236. throw;
  1237. }
  1238. catch (Exception sqle)
  1239. {
  1240. throw ADOExceptionHelper.Convert(Factory.SQLExceptionConverter, sqle,
  1241. "could not initialize a collection batch: "
  1242. + MessageHelper.InfoString(CollectionPersisters[0], ids), SqlString);
  1243. }
  1244. Log.Debug("done batch load");
  1245. }
  1246. /// <summary>
  1247. /// Called by subclasses that batch initialize collections
  1248. /// </summary>
  1249. protected void LoadCollectionSubselect(ISessionImplementor session, object[] ids, object[] parameterValues,
  1250. IType[] parameterTypes, IDictionary<string, TypedValue> namedParameters,
  1251. IType type)
  1252. {
  1253. try
  1254. {
  1255. DoQueryAndInitializeNonLazyCollections(session,
  1256. new QueryParameters(parameterTypes, parameterValues, namedParameters, ids),
  1257. true);
  1258. }
  1259. catch (HibernateException)
  1260. {
  1261. // Do not call Convert on HibernateExceptions
  1262. throw;
  1263. }
  1264. catch (Exception sqle)
  1265. {
  1266. throw ADOExceptionHelper.Convert(Factory.SQLExceptionConverter, sqle,
  1267. "could not load collection by subselect: "
  1268. + MessageHelper.InfoString(CollectionPersisters[0], ids), SqlString,
  1269. parameterValues, namedParameters);
  1270. }
  1271. }
  1272. /// <summary>
  1273. /// Return the query results, using the query cache, called
  1274. /// by subclasses that implement cacheable queries
  1275. /// </summary>
  1276. /// <param name="session"></param>
  1277. /// <param name="queryParameters"></param>
  1278. /// <param name="querySpaces"></param>
  1279. /// <param name="resultTypes"></param>
  1280. /// <returns></returns>
  1281. protected IList List(ISessionImplementor session, QueryParameters queryParameters, ISet<string> querySpaces, IType[] resultTypes)
  1282. {
  1283. bool cacheable = _factory.Settings.IsQueryCacheEnabled && queryParameters.Cacheable;
  1284. if (cacheable)
  1285. {
  1286. return ListUsingQueryCache(

Large files files are truncated, but you can click here to view the full file