PageRenderTime 58ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/src/NHibernate.Shards/src/NHibernate.Shards/Session/ShardedSessionImpl.cs

https://bitbucket.org/dabide/nhcontrib
C# | 2509 lines | 1553 code | 238 blank | 718 comment | 176 complexity | 8a4f8a8f66a263a88ade31517d953a4a MD5 | raw file
Possible License(s): BSD-3-Clause, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0, Apache-2.0, LGPL-3.0, LGPL-2.1
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Collections.ObjectModel;
  5. using System.Data;
  6. using System.Linq.Expressions;
  7. using System.Runtime.Serialization;
  8. using Iesi.Collections.Generic;
  9. using log4net;
  10. using System.Linq;
  11. using NHibernate.Engine;
  12. using NHibernate.Id;
  13. using NHibernate.Metadata;
  14. using NHibernate.Proxy;
  15. using NHibernate.Shards.Criteria;
  16. using NHibernate.Shards.Engine;
  17. using NHibernate.Shards.Id;
  18. using NHibernate.Shards.Query;
  19. using NHibernate.Shards.Stat;
  20. using NHibernate.Shards.Strategy;
  21. using NHibernate.Shards.Strategy.Exit;
  22. using NHibernate.Shards.Strategy.Selection;
  23. using NHibernate.Shards.Transaction;
  24. using NHibernate.Shards.Util;
  25. using NHibernate.Stat;
  26. using NHibernate.Type;
  27. namespace NHibernate.Shards.Session
  28. {
  29. /// <summary>
  30. /// Concrete implementation of a ShardedSession, and also the central component of
  31. /// Hibernate Shards' internal implementation. This class exposes two interfaces;
  32. /// ShardedSession itself, to the application, and ShardedSessionImplementor, to
  33. /// other components of Hibernate Shards. This class is not threadsafe.
  34. /// </summary>
  35. public class ShardedSessionImpl : IShardedSession, IShardedSessionImplementor, IShardIdResolver
  36. {
  37. [ThreadStatic]
  38. private static ShardId currentSubgraphShardId;
  39. private readonly bool checkAllAssociatedObjectsForDifferentShards;
  40. private readonly Set<System.Type> classesWithoutTopLevelSaveSupport;
  41. private readonly ILog log = LogManager.GetLogger(typeof(ShardedSessionImpl));
  42. private readonly IShardedSessionFactoryImplementor shardedSessionFactory;
  43. private readonly IDictionary<ShardId, IShard> shardIdsToShards;
  44. private readonly List<IShard> shards;
  45. private readonly IShardStrategy shardStrategy;
  46. private bool closed = false;
  47. private bool lockedShard = false;
  48. private ShardId lockedShardId;
  49. // access to sharded session is single-threaded so we can use a non-atomic
  50. // counter for criteria ids or query ids
  51. private int nextCriteriaId = 0;
  52. private int nextQueryId = 0;
  53. private IShardedTransaction transaction;
  54. /**
  55. * Constructor used for openSession(...) processing.
  56. *
  57. * @param shardedSessionFactory The factory from which this session was obtained
  58. * @param shardStrategy The shard strategy for this session
  59. * @param classesWithoutTopLevelSaveSupport The set of classes on which top-level save can not be performed
  60. * @param checkAllAssociatedObjectsForDifferentShards Should we check for cross-shard relationships
  61. */
  62. internal ShardedSessionImpl(
  63. IShardedSessionFactoryImplementor shardedSessionFactory,
  64. IShardStrategy shardStrategy,
  65. Set<System.Type> classesWithoutTopLevelSaveSupport,
  66. bool checkAllAssociatedObjectsForDifferentShards)
  67. : this(null,
  68. shardedSessionFactory,
  69. shardStrategy,
  70. classesWithoutTopLevelSaveSupport,
  71. checkAllAssociatedObjectsForDifferentShards)
  72. {
  73. }
  74. /**
  75. * Constructor used for openSession(...) processing.
  76. *
  77. * @param interceptor The interceptor to be applied to this session
  78. * @param shardedSessionFactory The factory from which this session was obtained
  79. * @param shardStrategy The shard strategy for this session
  80. * @param classesWithoutTopLevelSaveSupport The set of classes on which top-level save can not be performed
  81. * @param checkAllAssociatedObjectsForDifferentShards Should we check for cross-shard relationships
  82. */
  83. internal ShardedSessionImpl(
  84. /*@Nullable*/ IInterceptor interceptor,
  85. IShardedSessionFactoryImplementor shardedSessionFactory,
  86. IShardStrategy shardStrategy,
  87. Set<System.Type> classesWithoutTopLevelSaveSupport,
  88. bool checkAllAssociatedObjectsForDifferentShards)
  89. {
  90. this.shardedSessionFactory = shardedSessionFactory;
  91. shards =
  92. BuildShardListFromSessionFactoryShardIdMap(shardedSessionFactory.GetSessionFactoryShardIdMap(),
  93. checkAllAssociatedObjectsForDifferentShards, this, interceptor);
  94. shardIdsToShards = BuildShardIdsToShardsMap();
  95. this.shardStrategy = shardStrategy;
  96. this.classesWithoutTopLevelSaveSupport = classesWithoutTopLevelSaveSupport;
  97. this.checkAllAssociatedObjectsForDifferentShards = checkAllAssociatedObjectsForDifferentShards;
  98. }
  99. public static ShardId CurrentSubgraphShardId
  100. {
  101. get { return currentSubgraphShardId; }
  102. set { currentSubgraphShardId = value; }
  103. }
  104. public ISession SomeSession
  105. {
  106. get
  107. {
  108. foreach (IShard shard in shards)
  109. {
  110. if (shard.Session != null)
  111. {
  112. return shard.Session;
  113. }
  114. }
  115. return null;
  116. }
  117. }
  118. #region IShardedSession Members
  119. /// <summary>
  120. /// Gets the non-sharded session with which the objects is associated.
  121. /// </summary>
  122. /// <param name="obj">the object for which we want the Session</param>
  123. /// <returns>
  124. /// The Session with which this object is associated, or null if the
  125. /// object is not associated with a session belonging to this ShardedSession
  126. /// </returns>
  127. public ISession GetSessionForObject(object obj)
  128. {
  129. return GetSessionForObject(obj, shards);
  130. }
  131. /// <summary>
  132. /// Gets the ShardId of the shard with which the objects is associated.
  133. /// </summary>
  134. /// <param name="obj">the object for which we want the Session</param>
  135. /// <returns>
  136. /// the ShardId of the Shard with which this object is associated, or
  137. /// null if the object is not associated with a shard belonging to this
  138. /// ShardedSession
  139. /// </returns>
  140. public ShardId GetShardIdForObject(object obj)
  141. {
  142. return GetShardIdForObject(obj, shards);
  143. }
  144. /// <summary>
  145. /// Place the session into a state where every create operation takes place
  146. /// on the same shard. Once the shard is locked on a session it cannot
  147. /// be unlocked.
  148. /// </summary>
  149. public void LockShard()
  150. {
  151. lockedShard = true;
  152. }
  153. /// <summary>
  154. /// Force the <c>ISession</c> to flush.
  155. /// </summary>
  156. /// <remarks>
  157. /// Must be called at the end of a unit of work, before commiting the transaction and closing
  158. /// the session (<c>Transaction.Commit()</c> calls this method). <i>Flushing</i> if the process
  159. /// of synchronising the underlying persistent store with persistable state held in memory.
  160. /// </remarks>
  161. public void Flush()
  162. {
  163. foreach (IShard shard in shards)
  164. {
  165. // unopened sessions won't have anything to flush
  166. if (shard.Session != null)
  167. {
  168. shard.Session.Flush();
  169. }
  170. }
  171. }
  172. /// <summary>
  173. /// Determines at which points Hibernate automatically flushes the session.
  174. /// </summary>
  175. /// <remarks>
  176. /// For a readonly session, it is reasonable to set the flush mode to <c>FlushMode.Never</c>
  177. /// at the start of the session (in order to achieve some extra performance).
  178. /// </remarks>
  179. public FlushMode FlushMode
  180. {
  181. get
  182. {
  183. // all shards must have the same flush mode
  184. ISession someSession = SomeSession;
  185. if (someSession == null)
  186. {
  187. someSession = shards[0].EstablishSession();
  188. }
  189. return someSession.FlushMode;
  190. }
  191. set
  192. {
  193. var @event = new SetFlushModeOpenSessionEvent(value);
  194. foreach (IShard shard in shards)
  195. {
  196. if (shard.Session != null)
  197. {
  198. shard.Session.FlushMode = value;
  199. }
  200. else
  201. {
  202. shard.AddOpenSessionEvent(@event);
  203. }
  204. }
  205. }
  206. }
  207. /// <summary>
  208. /// The current cache mode.
  209. /// </summary>
  210. /// <remarks>
  211. /// Cache mode determines the manner in which this session can interact with
  212. /// the second level cache.
  213. /// </remarks>
  214. public CacheMode CacheMode
  215. {
  216. get
  217. {
  218. // all shards must have the same cache mode
  219. ISession someSession = SomeSession;
  220. if (someSession == null)
  221. {
  222. someSession = shards[0].EstablishSession();
  223. }
  224. return someSession.CacheMode;
  225. }
  226. set
  227. {
  228. var @event = new SetCacheModeOpenSessionEvent(value);
  229. foreach (IShard shard in shards)
  230. {
  231. if (shard.Session != null)
  232. {
  233. shard.Session.CacheMode = value;
  234. }
  235. else
  236. {
  237. shard.AddOpenSessionEvent(@event);
  238. }
  239. }
  240. }
  241. }
  242. /// <summary>
  243. /// Get the <see cref="ISessionFactory" /> that created this instance.
  244. /// </summary>
  245. public ISessionFactory SessionFactory
  246. {
  247. get { return shardedSessionFactory; }
  248. }
  249. /// <summary>
  250. /// Deprecated.
  251. /// </summary>
  252. public IDbConnection Connection
  253. {
  254. get { throw new InvalidOperationException("On Shards this is deprecated"); }
  255. }
  256. /// <summary>
  257. /// Disconnect the <c>ISession</c> from the current ADO.NET connection.
  258. /// </summary>
  259. /// <remarks>
  260. /// If the connection was obtained by Hibernate, close it or return it to the connection
  261. /// pool. Otherwise return it to the application. This is used by applications which require
  262. /// long transactions.
  263. /// </remarks>
  264. /// <returns>The connection provided by the application or <see langword="null" /></returns>
  265. public IDbConnection Disconnect()
  266. {
  267. foreach (IShard shard in shards)
  268. {
  269. if (shard.Session != null)
  270. {
  271. shard.Session.Disconnect();
  272. }
  273. }
  274. // we do not allow application-supplied connections, so we can always return
  275. // null
  276. return null;
  277. }
  278. /// <summary>
  279. /// Obtain a new ADO.NET connection.
  280. /// </summary>
  281. /// <remarks>
  282. /// This is used by applications which require long transactions
  283. /// </remarks>
  284. public void Reconnect()
  285. {
  286. throw new NotSupportedException();
  287. }
  288. /// <summary>
  289. /// Reconnect to the given ADO.NET connection.
  290. /// </summary>
  291. /// <remarks>This is used by applications which require long transactions</remarks>
  292. /// <param name="connection">An ADO.NET connection</param>
  293. public void Reconnect(IDbConnection connection)
  294. {
  295. throw new NotSupportedException("Cannot reconnect a sharded session");
  296. }
  297. /// <summary>
  298. /// End the <c>ISession</c> by disconnecting from the ADO.NET connection and cleaning up.
  299. /// </summary>
  300. /// <remarks>
  301. /// It is not strictly necessary to <c>Close()</c> the <c>ISession</c> but you must
  302. /// at least <c>Disconnect()</c> it.
  303. /// </remarks>
  304. /// <returns>The connection provided by the application or <see langword="null" /></returns>
  305. public IDbConnection Close()
  306. {
  307. List<Exception> thrown = null;
  308. foreach (IShard shard in shards)
  309. {
  310. if (shard.Session != null)
  311. {
  312. try
  313. {
  314. shard.Session.Close();
  315. }
  316. catch (Exception ex)
  317. {
  318. thrown = thrown ?? new List<Exception>();
  319. thrown.Add(ex);
  320. // we're going to try and close everything that was
  321. // opened
  322. }
  323. }
  324. }
  325. shards.Clear();
  326. shardIdsToShards.Clear();
  327. classesWithoutTopLevelSaveSupport.Clear();
  328. if (thrown != null && !(thrown.Count == 0))
  329. {
  330. // we'll just throw the first one
  331. Exception first = thrown[0];
  332. if (typeof(HibernateException).IsAssignableFrom(first.GetType()))
  333. {
  334. throw (HibernateException)first;
  335. }
  336. throw new HibernateException(first);
  337. }
  338. closed = true;
  339. // TODO what should I return here?
  340. return null;
  341. }
  342. /// <summary>
  343. /// Cancel execution of the current query.
  344. /// </summary>
  345. /// <remarks>
  346. /// May be called from one thread to stop execution of a query in another thread.
  347. /// Use with care!
  348. /// </remarks>
  349. public void CancelQuery()
  350. {
  351. // cancel across all shards
  352. foreach (IShard shard in shards)
  353. {
  354. if (shard.Session != null)
  355. {
  356. shard.Session.CancelQuery();
  357. }
  358. }
  359. }
  360. /// <summary>
  361. /// Is the <c>ISession</c> still open?
  362. /// </summary>
  363. public bool IsOpen
  364. {
  365. get
  366. {
  367. // one open session means the sharded session is open
  368. foreach(IShard shard in shards)
  369. {
  370. if(shard.Session != null && shard.Session.IsOpen)
  371. {
  372. return true;
  373. }
  374. }
  375. return !closed;
  376. }
  377. }
  378. /// <summary>
  379. /// Is the <c>ISession</c> currently connected?
  380. /// </summary>
  381. public bool IsConnected
  382. {
  383. get
  384. {
  385. // one connected shard means the session as a whole is connected
  386. foreach (IShard shard in shards)
  387. {
  388. if (shard.Session != null)
  389. {
  390. if (shard.Session.IsConnected)
  391. {
  392. return true;
  393. }
  394. }
  395. }
  396. return false;
  397. }
  398. }
  399. /// <summary>
  400. /// Does this <c>ISession</c> contain any changes which must be
  401. /// synchronized with the database? Would any SQL be executed if
  402. /// we flushed this session?
  403. /// </summary>
  404. public bool IsDirty()
  405. {
  406. // one dirty shard is all it takes
  407. foreach (IShard shard in shards)
  408. {
  409. if (shard.Session != null)
  410. if (shard.Session.IsDirty())
  411. return true;
  412. }
  413. return false;
  414. }
  415. /// <summary>
  416. /// Return the identifier of an entity instance cached by the <c>ISession</c>
  417. /// </summary>
  418. /// <remarks>
  419. /// Throws an exception if the instance is transient or associated with a different
  420. /// <c>ISession</c>
  421. /// </remarks>
  422. /// <param name="obj">a persistent instance</param>
  423. /// <returns>the identifier</returns>
  424. public object GetIdentifier(object obj)
  425. {
  426. foreach (IShard shard in shards)
  427. {
  428. if (shard.Session != null)
  429. {
  430. try
  431. {
  432. return shard.Session.GetIdentifier(obj);
  433. }
  434. catch (TransientObjectException e)
  435. {
  436. // Object is transient or is not associated with this session.
  437. }
  438. }
  439. }
  440. throw new TransientObjectException("Instance is transient or associated with a defferent Session");
  441. }
  442. /// <summary>
  443. /// Is this instance associated with this Session?
  444. /// </summary>
  445. /// <param name="obj">an instance of a persistent class</param>
  446. /// <returns>true if the given instance is associated with this Session</returns>
  447. public bool Contains(object obj)
  448. {
  449. foreach (IShard shard in shards)
  450. {
  451. if (shard.Session != null)
  452. if (shard.Session.Contains(obj))
  453. return true;
  454. }
  455. return false;
  456. }
  457. /// <summary>
  458. /// Remove this instance from the session cache.
  459. /// </summary>
  460. /// <remarks>
  461. /// Changes to the instance will not be synchronized with the database.
  462. /// This operation cascades to associated instances if the association is mapped
  463. /// with <c>cascade="all"</c> or <c>cascade="all-delete-orphan"</c>.
  464. /// </remarks>
  465. /// <param name="obj">a persistent instance</param>
  466. public void Evict(object obj)
  467. {
  468. foreach (IShard shard in shards)
  469. {
  470. if (shard.Session != null)
  471. {
  472. shard.Session.Evict(obj);
  473. }
  474. }
  475. }
  476. /// <summary>
  477. /// Return the persistent instance of the given entity class with the given identifier,
  478. /// obtaining the specified lock mode.
  479. /// </summary>
  480. /// <param name="clazz">A persistent class</param>
  481. /// <param name="id">A valid identifier of an existing persistent instance of the class</param>
  482. /// <param name="lockMode">The lock level</param>
  483. /// <returns>the persistent instance</returns>
  484. public object Load(System.Type clazz, object id, LockMode lockMode)
  485. {
  486. IList<ShardId> shardIds = SelectShardIdsFromShardResolutionStrategyData(
  487. new ShardResolutionStrategyDataImpl(clazz, id));
  488. if (shardIds.Count == 1)
  489. {
  490. return shardIdsToShards[shardIds[0]].EstablishSession().Load(clazz, id, lockMode);
  491. }
  492. else
  493. {
  494. Object result = Get(clazz, id, lockMode);
  495. if (result == null)
  496. {
  497. shardedSessionFactory.EntityNotFoundDelegate.HandleEntityNotFound(clazz.Name, id);
  498. }
  499. return result;
  500. }
  501. }
  502. public object Load(string entityName, object id, LockMode lockMode)
  503. {
  504. IList<ShardId> shardIds =
  505. SelectShardIdsFromShardResolutionStrategyData(new ShardResolutionStrategyDataImpl(entityName, id));
  506. if(shardIds.Count == 1)
  507. {
  508. return shardIdsToShards[shardIds[0]].EstablishSession().Load(entityName, id, lockMode);
  509. }
  510. object result = Get(entityName, id, lockMode);
  511. if(result == null)
  512. {
  513. shardedSessionFactory.EntityNotFoundDelegate.HandleEntityNotFound(entityName, id);
  514. }
  515. return result;
  516. }
  517. private object Get(string entityName, object id, LockMode mode)
  518. {
  519. IShardOperation<object> shardOp = new GetShardOperationByEntityNameIdAndLockMode(entityName, (ISerializable) id, mode);
  520. // we're not letting people customize shard selection by lockMode
  521. return ApplyGetOperation(shardOp, new ShardResolutionStrategyDataImpl(entityName, id));
  522. }
  523. /// <summary>
  524. /// Return the persistent instance of the given entity class with the given identifier,
  525. /// assuming that the instance exists.
  526. /// </summary>
  527. /// <remarks>
  528. /// You should not use this method to determine if an instance exists (use a query or
  529. /// <see cref="ISession.Get(Type,object)" /> instead). Use this only to retrieve an instance
  530. /// that you assume exists, where non-existence would be an actual error.
  531. /// </remarks>
  532. /// <param name="clazz">A persistent class</param>
  533. /// <param name="id">A valid identifier of an existing persistent instance of the class</param>
  534. /// <returns>The persistent instance or proxy</returns>
  535. public object Load(System.Type clazz, object id)
  536. {
  537. IList<ShardId> shardIds = SelectShardIdsFromShardResolutionStrategyData(new
  538. ShardResolutionStrategyDataImpl(clazz, id));
  539. if (shardIds.Count == 1)
  540. {
  541. return shardIdsToShards[shardIds[0]].EstablishSession().Load(clazz, id);
  542. }
  543. Object result = this.Get(clazz, id);
  544. if (result == null)
  545. {
  546. this.shardedSessionFactory.EntityNotFoundDelegate.HandleEntityNotFound(clazz.Name, id);
  547. }
  548. return result;
  549. }
  550. /// <summary>
  551. /// Return the persistent instance of the given entity class with the given identifier,
  552. /// obtaining the specified lock mode.
  553. /// </summary>
  554. /// <typeparam name="T">A persistent class</typeparam>
  555. /// <param name="id">A valid identifier of an existing persistent instance of the class</param>
  556. /// <param name="lockMode">The lock level</param>
  557. /// <returns>the persistent instance</returns>
  558. public T Load<T>(object id, LockMode lockMode)
  559. {
  560. return (T) Load(typeof (T), id, lockMode);
  561. }
  562. /// <summary>
  563. /// Return the persistent instance of the given entity class with the given identifier,
  564. /// assuming that the instance exists.
  565. /// </summary>
  566. /// <remarks>
  567. /// You should not use this method to determine if an instance exists (use a query or
  568. /// <see cref="ISession.Get{T}(object)" /> instead). Use this only to retrieve an instance that you
  569. /// assume exists, where non-existence would be an actual error.
  570. /// </remarks>
  571. /// <typeparam name="T">A persistent class</typeparam>
  572. /// <param name="id">A valid identifier of an existing persistent instance of the class</param>
  573. /// <returns>The persistent instance or proxy</returns>
  574. public T Load<T>(object id)
  575. {
  576. return (T) Load(typeof (T), id);
  577. }
  578. public object Load(string entityName, object id)
  579. {
  580. IList<ShardId> shardIds = SelectShardIdsFromShardResolutionStrategyData(new
  581. ShardResolutionStrategyDataImpl(entityName, id));
  582. if (shardIds.Count == 1)
  583. {
  584. return shardIdsToShards[shardIds[0]].EstablishSession().Load(entityName, id);
  585. }
  586. else
  587. {
  588. Object result = Get(entityName, id);
  589. if (result == null)
  590. {
  591. shardedSessionFactory.EntityNotFoundDelegate.HandleEntityNotFound(entityName, id);
  592. }
  593. return result;
  594. }
  595. }
  596. /// <summary>
  597. /// Read the persistent state associated with the given identifier into the given transient
  598. /// instance.
  599. /// </summary>
  600. /// <param name="obj">An "empty" instance of the persistent class</param>
  601. /// <param name="id">A valid identifier of an existing persistent instance of the class</param>
  602. public void Load(object obj, object id)
  603. {
  604. IList<ShardId> shardIds =
  605. SelectShardIdsFromShardResolutionStrategyData(new ShardResolutionStrategyDataImpl(obj.GetType(), id));
  606. if (shardIds.Count == 1)
  607. {
  608. shardIdsToShards[shardIds[0]].EstablishSession().Load(obj, id);
  609. }
  610. else
  611. {
  612. object result = Get(obj.GetType(), id);
  613. if (result == null)
  614. {
  615. shardedSessionFactory.EntityNotFoundDelegate.HandleEntityNotFound(obj.GetType().Name, id);
  616. }
  617. else
  618. {
  619. IShard objectShard = GetShardForObject(result, ShardIdListToShardList(shardIds));
  620. Evict(result);
  621. objectShard.EstablishSession().Load(obj, id);
  622. }
  623. }
  624. }
  625. /// <summary>
  626. /// Persist all reachable transient objects, reusing the current identifier
  627. /// values. Note that this will not trigger the Interceptor of the Session.
  628. /// </summary>
  629. /// <param name="obj"></param>
  630. /// <param name="replicationMode"></param>
  631. public void Replicate(object obj, ReplicationMode replicationMode)
  632. {
  633. Replicate(null, obj, replicationMode);
  634. }
  635. /// <summary>
  636. ///
  637. /// </summary>
  638. /// <param name="entityName"></param>
  639. /// <param name="obj"></param>
  640. /// <param name="replicationMode"></param>
  641. public void Replicate(string entityName, object obj, ReplicationMode replicationMode)
  642. {
  643. ISerializable id = ExtractId(obj);
  644. IList<ShardId> shardIds =
  645. SelectShardIdsFromShardResolutionStrategyData(new ShardResolutionStrategyDataImpl(obj.GetType(), id));
  646. if(shardIds.Count == 1)
  647. {
  648. CurrentSubgraphShardId = shardIds[0];
  649. shardIdsToShards[shardIds[0]].EstablishSession().Replicate(entityName, obj, replicationMode);
  650. }
  651. else
  652. {
  653. object result = null;
  654. if(id != null)
  655. {
  656. result = Get(obj.GetType(), id);
  657. }
  658. if(result == null)
  659. {
  660. ShardId shardId = SelectShardIdForNewObject(obj);
  661. CurrentSubgraphShardId = shardId;
  662. shardIdsToShards[shardId].EstablishSession().Replicate(entityName, obj, replicationMode);
  663. }
  664. else
  665. {
  666. IShard objectShard = GetShardForObject(result, ShardIdListToShardList(shardIds));
  667. Evict(result);
  668. objectShard.EstablishSession().Replicate(entityName, obj, replicationMode);
  669. }
  670. }
  671. }
  672. ISerializable ExtractId(object obj)
  673. {
  674. IClassMetadata cmd = shardedSessionFactory.GetClassMetadata(obj.GetType());
  675. // I'm just guessing about the EntityMode
  676. return (ISerializable) cmd.GetIdentifier(obj, EntityMode.Poco);
  677. }
  678. /// <summary>
  679. /// Persist the given transient instance, first assigning a generated identifier.
  680. /// </summary>
  681. /// <remarks>
  682. /// Save will use the current value of the identifier property if the <c>Assigned</c>
  683. /// generator is used.
  684. /// </remarks>
  685. /// <param name="obj">A transient instance of a persistent class</param>
  686. /// <returns>The generated identifier</returns>
  687. public object Save(object obj)
  688. {
  689. return Save(null, obj);
  690. }
  691. public object Save(string entityName, object obj)
  692. {
  693. // TODO: what if we have detached instance?
  694. ShardId shardId = GetShardIdForObject(obj);
  695. if (shardId == null)
  696. {
  697. shardId = SelectShardIdForNewObject(obj);
  698. }
  699. Preconditions.CheckNotNull(shardId);
  700. SetCurrentSubgraphShardId(shardId);
  701. log.Debug(String.Format("Saving object of type {0} to shard {1}", obj.GetType(), shardId));
  702. return shardIdsToShards[shardId].EstablishSession().Save(entityName, obj);
  703. }
  704. public static void SetCurrentSubgraphShardId(ShardId shardId)
  705. {
  706. currentSubgraphShardId = shardId;
  707. }
  708. /*
  709. * We already know that we don't have a shardId locked in for this session,
  710. * and we already know that this object can't grab its session from some
  711. * other object (we looked). If this class is in the set of classes
  712. * that don't support top-level saves, it's an error.
  713. * This is to prevent clients from accidentally splitting their object graphs
  714. * across multiple shards.
  715. */
  716. private void CheckForUnsupportedToplevelSave(System.Type clazz)
  717. {
  718. if(classesWithoutTopLevelSaveSupport.Contains(clazz))
  719. {
  720. string msg = string.Format("Attempt to save object of type {0} as top-level object", clazz.Name);
  721. log.Error(msg);
  722. throw new HibernateException(msg);
  723. }
  724. }
  725. ShardId SelectShardIdForNewObject(object obj)
  726. {
  727. if (lockedShardId != null)
  728. {
  729. return lockedShardId;
  730. }
  731. ShardId shardId;
  732. /*
  733. * Someone is trying to save this object, and that's wonderful, but if
  734. * this object references or is referenced by any other objects that have already been
  735. * associated with a session it's important that this object end up
  736. * associated with the same session. In order to make sure that happens,
  737. * we're going to look at the metadata for this object and see what
  738. * references we have, and then use those to determine the proper shard.
  739. * If we can't find any references we'll leave it up to the shard selection
  740. * strategy.
  741. */
  742. shardId = GetShardIdOfRelatedObject(obj);
  743. if(shardId == null)
  744. {
  745. CheckForUnsupportedToplevelSave(obj.GetType());
  746. shardId = shardStrategy.ShardSelectionStrategy.SelectShardIdForNewObject(obj);
  747. }
  748. // lock has been requested but shard has not yet been selected - lock it in
  749. if(lockedShard)
  750. {
  751. lockedShardId = shardId;
  752. }
  753. log.Debug(string.Format("Selected shard {0} for object of type {1}", shardId.Id, obj.GetType().Name));
  754. return shardId;
  755. }
  756. /**
  757. * TODO(maxr) I can see this method benefitting from a cache that lets us quickly
  758. * see which properties we might need to look at.
  759. */
  760. ShardId GetShardIdOfRelatedObject(object obj)
  761. {
  762. IClassMetadata cmd = GetClassMetadata(obj.GetType());
  763. var types = cmd.GetPropertyValues(obj, EntityMode.Poco); // this wasn't in java null, EntityMode.Poco
  764. // TODO() fix hard-coded entity mode
  765. object[] values = cmd.GetPropertyValues(obj, EntityMode.Poco);
  766. ShardId shardId = null;
  767. List<Collection<Object>> collections = null;
  768. foreach (KeyValuePair<IType, object> pair in CrossShardRelationshipDetectingInterceptor.BuildListOfAssociations(new IType[] {}, new object[] {}))//types, values
  769. {
  770. if (pair.Key.IsCollectionType) //pair.getFirst().isCollectionType()
  771. {
  772. /**
  773. * collection types are more expensive to evaluate (might involve
  774. * lazy-loading the contents of the collection from the db), so
  775. * let's hold off until the end on the chance that we can fail
  776. * quickly.
  777. */
  778. if (collections == null)
  779. {
  780. collections = new List<Collection<object>>();
  781. }
  782. var coll = (Collection<Object>)pair.Value;
  783. collections.Add(coll);
  784. }
  785. else
  786. {
  787. shardId = CheckForConflictingShardId(shardId, obj.GetType(), pair.Value);
  788. /**
  789. * if we're not checking for different shards, return as soon as we've
  790. * got one
  791. */
  792. if (shardId != null && !checkAllAssociatedObjectsForDifferentShards)
  793. {
  794. return shardId;
  795. }
  796. }
  797. }
  798. if (collections != null)
  799. {
  800. foreach (Object collEntry in Iterables.Concatenation(collections))
  801. {
  802. shardId = CheckForConflictingShardId(shardId, obj.GetType(), collEntry);
  803. if (shardId != null && !checkAllAssociatedObjectsForDifferentShards)
  804. {
  805. /**
  806. * if we're not checking for different shards, return as soon as we've
  807. * got one
  808. */
  809. return shardId;
  810. }
  811. }
  812. }
  813. return shardId;
  814. }
  815. ShardId CheckForConflictingShardId(ShardId existingShardId, System.Type newObjectClass, Object associatedObject)
  816. {
  817. ShardId localShardId = GetShardIdForObject(associatedObject);
  818. if (localShardId != null)
  819. {
  820. if (existingShardId == null)
  821. {
  822. existingShardId = localShardId;
  823. }
  824. else if (!localShardId.Equals(existingShardId))
  825. {
  826. /*readonly*/
  827. string msg = string.Format(
  828. "Object of type {0} is on shard {1} but an associated object of type {2} is on shard {3}.",
  829. newObjectClass.Name,
  830. existingShardId.Id,
  831. associatedObject.GetType().Name,
  832. localShardId.Id);
  833. log.Error(msg);
  834. throw new CrossShardAssociationException(msg);
  835. }
  836. }
  837. return existingShardId;
  838. }
  839. IClassMetadata GetClassMetadata(System.Type clazz)
  840. {
  841. return GetShardedSessionFactory().GetClassMetadata(clazz);
  842. }
  843. private IShardedSessionFactoryImplementor GetShardedSessionFactory()
  844. {
  845. return shardedSessionFactory; // why when I set as property expects a Delegate?
  846. }
  847. /// <summary>
  848. /// Persist the given transient instance, using the given identifier.
  849. /// </summary>
  850. /// <param name="obj">A transient instance of a persistent class</param>
  851. /// <param name="id">An unused valid identifier</param>
  852. public void Save(object obj, object id)
  853. {
  854. throw new NotSupportedException();
  855. }
  856. /// <summary>
  857. /// Either <c>Save()</c> or <c>Update()</c> the given instance, depending upon the value of
  858. /// its identifier property.
  859. /// </summary>
  860. /// <remarks>
  861. /// By default the instance is always saved. This behaviour may be adjusted by specifying
  862. /// an <c>unsaved-value</c> attribute of the identifier property mapping
  863. /// </remarks>
  864. /// <param name="obj">A transient instance containing new or updated state</param>
  865. public void SaveOrUpdate(object obj)
  866. {
  867. ApplySaveOrUpdateOperation(new SaveOrUpdateSimple(), obj);
  868. }
  869. void ApplySaveOrUpdateOperation(ISaveOrUpdateOperation op, object obj)
  870. {
  871. ShardId shardId = GetShardIdForObject(obj);
  872. if(shardId != null)
  873. {
  874. // attached object
  875. op.SaveOrUpdate(shardIdsToShards[shardId], obj);
  876. return;
  877. }
  878. IList<IShard> potentialShards = DetermineShardsObjectViaResolutionStrategy(obj);
  879. if(potentialShards.Count == 1)
  880. {
  881. op.SaveOrUpdate(potentialShards[0], obj);
  882. return;
  883. }
  884. /**
  885. * Too bad, we've got a detached object that could be on more than 1 shard.
  886. * The only safe way to handle this is to try and lookup the object, and if
  887. * it exists, do a merge, and if it doesn't, do a save.
  888. */
  889. ISerializable id = ExtractId(obj);
  890. if(id != null)
  891. {
  892. object persistent = Get(obj.GetType(), id);
  893. if(persistent != null)
  894. {
  895. shardId = GetShardIdForObject(persistent);
  896. }
  897. }
  898. if(shardId != null)
  899. {
  900. op.Merge(shardIdsToShards[shardId], obj);
  901. }
  902. else
  903. {
  904. Save(obj);
  905. }
  906. }
  907. IList<IShard> DetermineShardsObjectViaResolutionStrategy(object obj)
  908. {
  909. ISerializable id = ExtractId(obj);
  910. if(id == null)
  911. {
  912. return new List<IShard>();
  913. }
  914. var srsd = new ShardResolutionStrategyDataImpl(obj.GetType(), id);
  915. IList<ShardId> shardIds = SelectShardIdsFromShardResolutionStrategyData(srsd);
  916. return ShardIdListToShardList(shardIds);
  917. }
  918. public void SaveOrUpdate(string entityName, object obj)
  919. {
  920. ISaveOrUpdateOperation so = new SaveOrUpdateWithEntityName(entityName);
  921. ApplySaveOrUpdateOperation(so, obj);
  922. }
  923. /// <summary>
  924. /// Update the persistent instance with the identifier of the given transient instance.
  925. /// </summary>
  926. /// <remarks>
  927. /// If there is a persistent instance with the same identifier, an exception is thrown. If
  928. /// the given transient instance has a <see langword="null" /> identifier, an exception will be thrown.
  929. /// </remarks>
  930. /// <param name="obj">A transient instance containing updated state</param>
  931. public void Update(object obj)
  932. {
  933. IUpdateOperation updateOperation = new UpdateOperationSimple();
  934. ApplyUpdateOperation(updateOperation, obj);
  935. }
  936. private void ApplyUpdateOperation(IUpdateOperation updateOperation, object obj)
  937. {
  938. ShardId shardId = GetShardIdForObject(obj);
  939. if(shardId != null)
  940. {
  941. // attached object
  942. updateOperation.Update(shardIdsToShards[shardId], obj);
  943. return;
  944. }
  945. IList<IShard> potentialShards = DetermineShardsObjectViaResolutionStrategy(obj);
  946. if(potentialShards.Count == 1)
  947. {
  948. updateOperation.Update(potentialShards[0], obj);
  949. return;
  950. }
  951. /**
  952. * Too bad, we've got a detached object that could be on more than 1 shard.
  953. * The only safe way to perform the update is to load the object and then
  954. * do a merge.
  955. */
  956. ISerializable id = ExtractId(obj);
  957. if(id != null)
  958. {
  959. object persistent = Get(obj.GetType(), id);
  960. if(persistent != null)
  961. {
  962. shardId = GetShardIdForObject(persistent);
  963. }
  964. }
  965. if(shardId == null)
  966. {
  967. /**
  968. * This is an error condition. In order to provide the same behavior
  969. * as a non-sharded session we're just going to dispatch the update
  970. * to a random shard (we know it will fail because either we don't have
  971. * an id or the lookup returned).
  972. */
  973. updateOperation.Update(Shards[0], obj);
  974. // this call may succeed but the commit will fail
  975. }
  976. else
  977. {
  978. updateOperation.Merge(shardIdsToShards[shardId], obj);
  979. }
  980. }
  981. /// <summary>
  982. /// Update the persistent state associated with the given identifier.
  983. /// </summary>
  984. /// <remarks>
  985. /// An exception is thrown if there is a persistent instance with the same identifier
  986. /// in the current session.
  987. /// </remarks>
  988. /// <param name="obj">A transient instance containing updated state</param>
  989. /// <param name="id">Identifier of persistent instance</param>
  990. public void Update(object obj, object id)
  991. {
  992. throw new NotSupportedException();
  993. }
  994. public void Update(string entityName, object obj)
  995. {
  996. IUpdateOperation updateOperation = new UpdateOperationWithEntityName(entityName);
  997. ApplyUpdateOperation(updateOperation, obj);
  998. }
  999. public object Merge(object obj)
  1000. {
  1001. return Merge(null, obj);
  1002. }
  1003. public object Merge(string entityName, object obj)
  1004. {
  1005. ISerializable id = ExtractId(obj);
  1006. IList<ShardId> shardIds =
  1007. SelectShardIdsFromShardResolutionStrategyData(new ShardResolutionStrategyDataImpl(obj.GetType(), id));
  1008. if(shardIds.Count == 1)
  1009. {
  1010. SetCurrentSubgraphShardId(shardIds[0]);
  1011. return shardIdsToShards[shardIds[0]].EstablishSession().Merge(entityName, obj);
  1012. }
  1013. object result = null;
  1014. if(id != null)
  1015. {
  1016. result = Get(obj.GetType(), id);
  1017. }
  1018. if(result == null)
  1019. {
  1020. ShardId shardId = SelectShardIdForNewObject(obj);
  1021. SetCurrentSubgraphShardId(shardId);
  1022. return shardIdsToShards[shardId].EstablishSession().Merge(entityName, obj);
  1023. }
  1024. IShard objectShard = GetShardForObject(result, ShardIdListToShardList(shardIds));
  1025. return objectShard.EstablishSession().Merge(entityName, obj);
  1026. }
  1027. public void Persist(object obj)
  1028. {
  1029. Persist(null, obj);
  1030. }
  1031. public void Persist(string entityName, object obj)
  1032. {
  1033. //TODO: what if we have detached object?
  1034. ShardId shardId = GetShardIdForObject(obj);
  1035. if(shardId == null)
  1036. {
  1037. shardId = SelectShardIdForNewObject(obj);
  1038. }
  1039. Preconditions.CheckNotNull(shardId);
  1040. SetCurrentSubgraphShardId(shardId);
  1041. log.Debug(string.Format("Persisting object of type {0} to shard {1}", obj.GetType(), shardId));
  1042. shardIdsToShards[shardId].EstablishSession().Persist(entityName, obj);
  1043. }
  1044. /// <summary>
  1045. /// Copy the state of the given object onto the persistent object with the same
  1046. /// identifier. If there is no persistent instance currently associated with
  1047. /// the session, it will be loaded. Return the persistent instance. If the
  1048. /// given instance is unsaved or does not exist in the database, save it and
  1049. /// return it as a newly persistent instance. Otherwise, the given instance
  1050. /// does not become associated with the session.
  1051. /// </summary>
  1052. /// <param name="obj">a transient instance with state to be copied</param>
  1053. /// <returns>an updated persistent instance</returns>
  1054. public object SaveOrUpdateCopy(object obj)
  1055. {
  1056. throw new NotSupportedException();
  1057. }
  1058. /// <summary>
  1059. /// Copy the state of the given object onto the persistent object with the
  1060. /// given identifier. If there is no persistent instance currently associated
  1061. /// with the session, it will be loaded. Return the persistent instance. If
  1062. /// there is no database row with the given identifier, save the given instance
  1063. /// and return it as a newly persistent instance. Otherwise, the given instance
  1064. /// does not become associated with the session.
  1065. /// </summary>
  1066. /// <param name="obj">a persistent or transient instance with state to be copied</param>
  1067. /// <param name="id">the identifier of the instance to copy to</param>
  1068. /// <returns>an updated persistent instance</returns>
  1069. public object SaveOrUpdateCopy(object obj, object id)
  1070. {
  1071. throw new NotSupportedException();
  1072. }
  1073. /// <summary>
  1074. /// Remove a persistent instance from the datastore.
  1075. /// </summary>
  1076. /// <remarks>
  1077. /// The argument may be an instance associated with the receiving <c>ISession</c> or a
  1078. /// transient instance with an identifier associated with existing persistent state.
  1079. /// </remarks>
  1080. /// <param name="obj">The instance to be removed</param>
  1081. public void Delete(object obj)
  1082. {
  1083. IDeleteOperation deleteOperation = new DeleteOperationSimple();
  1084. ApplyDeleteOperation(deleteOperation, obj);
  1085. }
  1086. public void Delete(string entityName, object obj)
  1087. {
  1088. IDeleteOperation deleteOperation = new DeleteOperationWithEntityName(entityName);
  1089. ApplyDeleteOperation(deleteOperation, obj);
  1090. }
  1091. private void ApplyDeleteOperation(IDeleteOperation deleteOperation, object obj)
  1092. {
  1093. ShardId shardId = GetShardIdForObject(obj);
  1094. if (shardId != null)
  1095. {
  1096. // attached object
  1097. deleteOperation.Delete(shardIdsToShards[shardId], obj);
  1098. return;
  1099. }
  1100. /**
  1101. * Detached object.
  1102. * We can't just try to delete on each shard because if you have an
  1103. * object associated with Session x and you try to delete that object in
  1104. * Session y, and if that object has persistent collections, Hibernate will
  1105. * blow up because it will try to associate the persistent collection with
  1106. * a different Session as part of the cascade. In order to avoid this we
  1107. * need to be precise about the shard on which we perform the delete.
  1108. *
  1109. * First let's see if we can derive the shard just from the object's id.
  1110. */
  1111. IList<IShard> potentialShards = DetermineShardsObjectViaResolutionStrategy(obj);
  1112. if(potentialShards.Count == 1)
  1113. {
  1114. deleteOperation.Delete(potentialShards[0], obj);
  1115. return;
  1116. }
  1117. /**
  1118. * Too bad, we've got a detached object that could be on more than 1 shard.
  1119. * The only safe way to perform the delete is to load the object before
  1120. * deleting.
  1121. */
  1122. object persistent = Get(obj.GetType(), ExtractId(obj));
  1123. shardId = GetShardIdForObject(persistent);
  1124. deleteOperation.Delete(shardIdsToShards[shardId], persistent);
  1125. }
  1126. /// <summary>
  1127. /// Execute a query
  1128. /// </summary>
  1129. /// <param name="query">A query expressed in Hibernate's query language</param>
  1130. /// <returns>A distinct list of instances</returns>
  1131. /// <remarks>See <see cref="IQuery.List()"/> for implications of <c>cache</c> usage.</remarks>
  1132. public IList Find(string query)
  1133. {
  1134. throw new NotSupportedException();
  1135. }
  1136. /// <summary>
  1137. /// Execute a query, binding a value to a "?" parameter in the query string.
  1138. /// </summary>
  1139. /// <param name="query">The query string</param>
  1140. /// <param name="value">A value to be bound to a "?" placeholder</param>
  1141. /// <param name="type">The Hibernate type of the value</param>
  1142. /// <returns>A distinct list of instances</returns>
  1143. /// <remarks>See <see cref="IQuery.List()"/> for implications of <c>cache</c> usage.</remarks>
  1144. public IList Find(string query, object value, IType type)
  1145. {
  1146. throw new NotSupportedException();
  1147. }
  1148. /// <summary>
  1149. /// Execute a query, binding an array of values to a "?" parameters in the query string.
  1150. /// </summary>
  1151. /// <param name="query">The query string</param>
  1152. /// <param name="values">An array of values to be bound to the "?" placeholders</param>
  1153. /// <param name="types">An array of Hibernate types of the values</param>
  1154. /// <returns>A distinct list of instances</returns>
  1155. /// <remarks>See <see cref="IQuery.List()"/> for implications of <c>cache</c> usage.</remarks>
  1156. public IList Find(string query, object[] values, IType[] types)
  1157. {
  1158. throw new NotSupportedException();
  1159. }
  1160. /// <summary>
  1161. /// Execute a query and return the results in an interator.
  1162. /// </summary>
  1163. /// <remarks>
  1164. /// <para>
  1165. /// If the query has multiple return values, values will be returned in an array of
  1166. /// type <c>object[]</c>.
  1167. /// </para>
  1168. /// <para>
  1169. /// Entities returned as results are initialized on demand. The first SQL query returns
  1170. /// identifiers only. So <c>Enumerator()</c> is usually a less efficient way to retrieve
  1171. /// object than <c>List()</c>.
  1172. /// </para>
  1173. /// </remarks>
  1174. /// <param name="query">The query string</param>
  1175. /// <returns>An enumerator</returns>
  1176. public IEnumerable Enumerable(string query)
  1177. {
  1178. throw new NotSupportedException();
  1179. }
  1180. /// <summary>
  1181. /// Execute a query and return the results in an interator,
  1182. /// binding a value to a "?" parameter in the query string.
  1183. /// </summary>
  1184. /// <remarks>
  1185. /// <para>
  1186. /// If the query has multiple return values, values will be returned in an array of
  1187. /// type <c>object[]</c>.
  1188. /// </para>
  1189. /// <para>
  1190. /// Entities returned as results are initialized on demand. The first SQL query returns
  1191. /// identifiers only. So <c>Enumerator()</c> is usually a less efficient way to retrieve
  1192. /// object than <c>List()</c>.
  1193. /// </para>
  1194. /// </remarks>
  1195. /// <param name="query">The query string</param>
  1196. /// <param name="value">A value to be written to a "?" placeholder in the query string</param>
  1197. /// <param name="type">The hibernate type of the value</param>
  1198. /// <returns>An enumerator</returns>
  1199. public IEnumerable Enumerable(string query, object value, IType type)
  1200. {
  1201. throw new NotSupportedException();
  1202. }
  1203. /// <summary>
  1204. /// Execute a query and return the results in an interator,
  1205. /// binding the values to "?"s parameters in the query string.
  1206. /// </summary>
  1207. /// <remarks>
  1208. /// <para>
  1209. /// If the query has multiple return values, values will be returned in an array of
  1210. /// type <c>object[]</c>.
  1211. /// </para>
  1212. /// <para>
  1213. /// Entities returned as results are initialized on demand. The first SQL query returns
  1214. /// identifiers only. So <c>Enumerator()</c> is usually a less efficient way to retrieve
  1215. /// object than <c>List()</c>.
  1216. /// </para>
  1217. /// </remarks>
  1218. /// <param name="query">The query string</param>
  1219. /// <param name="values">A list of values to be written to "?" placeholders in the query</param>
  1220. /// <param name="types">A list of hibernate types of the values</param>
  1221. /// <returns>An enumerator</returns>
  1222. public IEnumerable Enumerable(string query, object[] values, IType[] types)
  1223. {
  1224. throw new NotSupportedException();
  1225. }
  1226. /// <summary>
  1227. /// Apply a filter to a persistent collection.
  1228. /// </summary>
  1229. /// <remarks>
  1230. /// A filter is a Hibernate query that may refer to <c>this</c>, the collection element.
  1231. /// Filters allow efficient access to very large lazy collections. (Executing the filter
  1232. /// does not initialize the collection.)
  1233. /// </remarks>
  1234. /// <param name="collection">A persistent collection to filter</param>
  1235. /// <param name="filter">A filter query string</param>
  1236. /// <returns>The resulting collection</returns>
  1237. public ICollection Filter(object collection, string filter)
  1238. {
  1239. throw new NotSupportedException();
  1240. }
  1241. /// <summary>
  1242. /// Apply a filter to a persistent collection, binding the given parameter to a "?" placeholder
  1243. /// </summary>
  1244. /// <remarks>
  1245. /// A filter is a Hibernate query that may refer to <c>this</c>, the collection element.
  1246. /// Filters allow efficient access to very large lazy collections. (Executing the filter
  1247. /// does not initialize the collection.)
  1248. /// </remarks>
  1249. /// <param name="collection">A persistent collection to filter</param>
  1250. /// <param name="filter">A filter query string</param>
  1251. /// <param name="value">A value to be written to a "?" placeholder in the query</param>
  1252. /// <param name="type">The hibernate type of value</param>
  1253. /// <returns>A collection</returns>
  1254. public ICollection Filter(object collection, string filter, object value, IType type)
  1255. {
  1256. throw new NotSupportedException();
  1257. }
  1258. /// <summary>
  1259. /// Apply a filter to a persistent collection, binding the given parameters to "?" placeholders.
  1260. /// </summary>
  1261. /// <remarks>
  1262. /// A filter is a Hibernate query that may refer to <c>this</c>, the collection element.
  1263. /// Filters allow efficient access to very large lazy collections. (Executing the filter
  1264. /// does not initialize the collection.)
  1265. /// </remarks>
  1266. /// <param name="collection">A persistent collection to filter</param>
  1267. /// <param name="filter">A filter query string</param>
  1268. /// <param name="values">The values to be written to "?" placeholders in the query</param>
  1269. /// <param name="types">The hibernate types of the values</param>
  1270. /// <returns>A collection</returns>
  1271. public ICollection Filter(object collection, string filter, object[] values, IType[] types)
  1272. {
  1273. throw new NotSupportedException();
  1274. }
  1275. /// <summary>
  1276. /// Delete all objects returned by the query.
  1277. /// </summary>
  1278. /// <param name="query">The query string</param>
  1279. /// <returns>Returns the number of objects deleted.</returns>
  1280. public int Delete(string query)
  1281. {
  1282. throw new NotSupportedException();
  1283. }
  1284. /// <summary>
  1285. /// Delete all objects returned by the query.
  1286. /// </summary>
  1287. /// <param name="query">The query string</param>
  1288. /// <param name="value">A value to be written to a "?" placeholer in the query</param>
  1289. /// <param name="type">The hibernate type of value.</param>
  1290. /// <returns>The number of instances deleted</returns>
  1291. public int Delete(string query, object value, IType type)
  1292. {
  1293. throw new NotSupportedException();
  1294. }
  1295. /// <summary>
  1296. /// Delete all objects returned by the query.
  1297. /// </summary>
  1298. /// <param name="query">The query string</param>
  1299. /// <param name="values">A list of values to be written to "?" placeholders in the query</param>
  1300. /// <param name="types">A list of Hibernate types of the values</param>
  1301. /// <returns>The number of instances deleted</returns>
  1302. public int Delete(string query, object[] values, IType[] types)
  1303. {
  1304. throw new NotSupportedException();
  1305. }
  1306. /// <summary>
  1307. /// Obtain the specified lock level upon the given object.
  1308. /// </summary>
  1309. /// <param name="obj">A persistent instance</param>
  1310. /// <param name="lockMode">The lock level</param>
  1311. public void Lock(object obj, LockMode lockMode)
  1312. {
  1313. IShardOperation<object> shardOp = new LockShardOperationByObjectAndLockMode(obj, lockMode);
  1314. InvokeOnShardWithObject(shardOp, obj);
  1315. }
  1316. public void Lock(string entityName, object obj, LockMode lockMode)
  1317. {
  1318. IShardOperation<object> shardOp = new LockShardOperationByEntityNameObjectLockMode(entityName, obj, lockMode);
  1319. InvokeOnShardWithObject(shardOp, obj);
  1320. }
  1321. /// <summary>
  1322. /// Re-read the state of the given instance from the underlying database.
  1323. /// </summary>
  1324. /// <remarks>
  1325. /// <para>
  1326. /// It is inadvisable to use this to implement long-running sessions that span many
  1327. /// business tasks. This method is, however, useful in certain special circumstances.
  1328. /// </para>
  1329. /// <para>
  1330. /// For example,
  1331. /// <list>
  1332. /// <item>Where a database trigger alters the object state upon insert or update</item>
  1333. /// <item>After executing direct SQL (eg. a mass update) in the same session</item>
  1334. /// <item>After inserting a <c>Blob</c> or <c>Clob</c></item>
  1335. /// </list>
  1336. /// </para>
  1337. /// </remarks>
  1338. /// <param name="obj">A persistent instance</param>
  1339. public void Refresh(object obj)
  1340. {
  1341. IRefreshOperation refreshOperation = new RefreshOperationSimple();
  1342. ApplyRefreshOperation(refreshOperation, obj);
  1343. }
  1344. private void ApplyRefreshOperation(IRefreshOperation refreshOperation, object obj)
  1345. {
  1346. ShardId shardId = GetShardIdForObject(obj);
  1347. if(shardId != null)
  1348. {
  1349. refreshOperation.Refresh(shardIdsToShards[shardId], obj);
  1350. }
  1351. else
  1352. {
  1353. IList<IShard> candidateShards = DetermineShardsObjectViaResolutionStrategy(obj);
  1354. if (candidateShards.Count == 1)
  1355. {
  1356. refreshOperation.Refresh(candidateShards[0], obj);
  1357. }
  1358. else
  1359. {
  1360. foreach(IShard shard in candidateShards)
  1361. {
  1362. try
  1363. {
  1364. refreshOperation.Refresh(shard, obj);
  1365. }
  1366. catch(UnresolvableObjectException)
  1367. {
  1368. //ignore
  1369. }
  1370. }
  1371. refreshOperation.Refresh(shards[0], obj);
  1372. }
  1373. }
  1374. }
  1375. /// <summary>
  1376. /// Re-read the state of the given instance from the underlying database, with
  1377. /// the given <c>LockMode</c>.
  1378. /// </summary>
  1379. /// <remarks>
  1380. /// It is inadvisable to use this to implement long-running sessions that span many
  1381. /// business tasks. This method is, however, useful in certain special circumstances.
  1382. /// </remarks>
  1383. /// <param name="obj">a persistent or transient instance</param>
  1384. /// <param name="lockMode">the lock mode to use</param>
  1385. public void Refresh(object obj, LockMode lockMode)
  1386. {
  1387. IRefreshOperation refreshOperation = new RefreshOperationWithLockMode(lockMode);
  1388. ApplyRefreshOperation(refreshOperation, obj);
  1389. }
  1390. /// <summary>
  1391. /// Determine the current lock mode of the given object
  1392. /// </summary>
  1393. /// <param name="obj">A persistent instance</param>
  1394. /// <returns>The current lock mode</returns>
  1395. public LockMode GetCurrentLockMode(object obj)
  1396. {
  1397. IShardOperation<LockMode> shardOp = new CurrentLockModeShardOperation(obj);
  1398. return InvokeOnShardWithObject(shardOp, obj);
  1399. }
  1400. /// <summary>
  1401. /// Begin a unit of work and return the associated <c>ITransaction</c> object.
  1402. /// </summary>
  1403. /// <remarks>
  1404. /// If a new underlying transaction is required, begin the transaction. Otherwise
  1405. /// continue the new work in the context of the existing underlying transaction.
  1406. /// The class of the returned <see cref="ITransaction" /> object is determined by
  1407. /// the property <c>transaction_factory</c>
  1408. /// </remarks>
  1409. /// <returns>A transaction instance</returns>
  1410. public ITransaction BeginTransaction()
  1411. {
  1412. ErrorIfClosed();
  1413. ITransaction result = GetTransaction(IsolationLevel.Unspecified);
  1414. result.Begin();
  1415. return result;
  1416. }
  1417. public ITransaction GetTransaction(IsolationLevel isoLevel)
  1418. {
  1419. ErrorIfClosed();
  1420. if (transaction == null)
  1421. {
  1422. transaction = new ShardedTransactionImpl(this,isoLevel);
  1423. }
  1424. return transaction;
  1425. }
  1426. void ErrorIfClosed()
  1427. {
  1428. if (closed)
  1429. {
  1430. throw new SessionException("Session is closed!");
  1431. }
  1432. }
  1433. /// <summary>
  1434. /// Begin a transaction with the specified <c>isolationLevel</c>
  1435. /// </summary>
  1436. /// <param name="isolationLevel">Isolation level for the new transaction</param>
  1437. /// <returns>A transaction instance having the specified isolation level</returns>
  1438. public ITransaction BeginTransaction(IsolationLevel isolationLevel)
  1439. {
  1440. ErrorIfClosed();
  1441. ITransaction result = GetTransaction(isolationLevel);
  1442. result.Begin();
  1443. return result;
  1444. }
  1445. public ICriteria CreateCriteria<T>() where T : class
  1446. {
  1447. return CreateCriteria(typeof (T));
  1448. }
  1449. public ICriteria CreateCriteria<T>(string alias) where T : class
  1450. {
  1451. return CreateCriteria(typeof (T), alias);
  1452. }
  1453. /// <summary>
  1454. /// Get the current Unit of Work and return the associated <c>ITransaction</c> object.
  1455. /// </summary>
  1456. public ITransaction Transaction
  1457. {
  1458. get
  1459. {
  1460. ErrorIfClosed();
  1461. if(transaction == null)
  1462. {
  1463. transaction = new ShardedTransactionImpl(this);
  1464. }
  1465. return transaction;
  1466. }
  1467. }
  1468. /// <summary>
  1469. /// Creates a new <c>Criteria</c> for the entity class.
  1470. /// </summary>
  1471. /// <param name="persistentClass">The class to Query</param>
  1472. /// <returns>An ICriteria object</returns>
  1473. public ICriteria CreateCriteria(System.Type persistentClass)
  1474. {
  1475. return new ShardedCriteriaImpl(new CriteriaId(nextCriteriaId++), shards,
  1476. new CriteriaFactoryImpl(persistentClass), shardStrategy.ShardAccessStrategy);
  1477. }
  1478. /// <summary>
  1479. /// Creates a new <c>Criteria</c> for the entity class with a specific alias
  1480. /// </summary>
  1481. /// <param name="persistentClass">The class to Query</param>
  1482. /// <param name="alias">The alias of the entity</param>
  1483. /// <returns>An ICriteria object</returns>
  1484. public ICriteria CreateCriteria(System.Type persistentClass, string alias)
  1485. {
  1486. return new ShardedCriteriaImpl(new CriteriaId(nextCriteriaId++), shards,
  1487. new CriteriaFactoryImpl(persistentClass, alias), shardStrategy.ShardAccessStrategy);
  1488. }
  1489. public ICriteria CreateCriteria(string entityName)
  1490. {
  1491. return new ShardedCriteriaImpl(new CriteriaId(nextCriteriaId++), shards,
  1492. new CriteriaFactoryImpl(entityName), shardStrategy.ShardAccessStrategy);
  1493. }
  1494. public ICriteria CreateCriteria(string entityName, string alias)
  1495. {
  1496. return new ShardedCriteriaImpl(new CriteriaId(nextCriteriaId++), shards,
  1497. new CriteriaFactoryImpl(entityName,alias), shardStrategy.ShardAccessStrategy);
  1498. }
  1499. public IQueryOver<T> QueryOver<T>() where T : class
  1500. {
  1501. throw new NotSupportedException();
  1502. }
  1503. public IQueryOver<T> QueryOver<T>(Expression<Func<T>> alias) where T : class
  1504. {
  1505. throw new NotSupportedException();
  1506. }
  1507. /// <summary>
  1508. /// Create a new instance of <c>Query</c> for the given query string
  1509. /// </summary>
  1510. /// <param name="queryString">A hibernate query string</param>
  1511. /// <returns>The query</returns>
  1512. public IQuery CreateQuery(string queryString)
  1513. {
  1514. return new ShardedQueryImpl(new QueryId(nextQueryId++), shards, new AdHocQueryFactoryImpl(queryString),
  1515. shardStrategy.ShardAccessStrategy);
  1516. }
  1517. //public IQuery CreateQuery(IQueryExpression queryExpression)
  1518. //{
  1519. // throw new NotSupportedException();
  1520. //}
  1521. /// <summary>
  1522. /// Create a new instance of <c>Query</c> for the given collection and filter string
  1523. /// </summary>
  1524. /// <param name="collection">A persistent collection</param>
  1525. /// <param name="queryString">A hibernate query</param>
  1526. /// <returns>A query</returns>
  1527. public IQuery CreateFilter(object collection, string queryString)
  1528. {
  1529. IShard shard = GetShardForCollection(collection,shards);
  1530. ISession session;
  1531. if(shard == null)
  1532. {
  1533. // collection not associated with any of our shards, so just delegate to
  1534. // a random shard. We'll end up failing, but we'll fail with the
  1535. // error that users typically get.
  1536. session = SomeSession;
  1537. if(session!=null )
  1538. {
  1539. session = shards[0].EstablishSession();
  1540. }
  1541. }
  1542. else
  1543. {
  1544. session = shard.EstablishSession();
  1545. }
  1546. return session.CreateFilter(collection, queryString);
  1547. }
  1548. private IShard GetShardForCollection(object collection, IList<IShard> shardsToConsider)
  1549. {
  1550. foreach(IShard shard in shardsToConsider)
  1551. {
  1552. if(shard.Session != null)
  1553. {
  1554. var si = (ISessionImplementor) shard.Session;
  1555. if(si.PersistenceContext.GetCollectionEntryOrNull(collection) != null)
  1556. {
  1557. return shard;
  1558. }
  1559. }
  1560. }
  1561. return null;
  1562. }
  1563. /// <summary>
  1564. /// Obtain an instance of <see cref="IQuery" /> for a named query string defined in the
  1565. /// mapping file.
  1566. /// </summary>
  1567. /// <param name="queryName">The name of a query defined externally.</param>
  1568. /// <returns>An <see cref="IQuery"/> from a named query string.</returns>
  1569. /// <remarks>
  1570. /// The query can be either in <c>HQL</c> or <c>SQL</c> format.
  1571. /// </remarks>
  1572. public IQuery GetNamedQuery(string queryName)
  1573. {
  1574. return new ShardedQueryImpl(new QueryId(nextQueryId++), shards, new NamedQueryFactoryImpl(queryName),
  1575. shardStrategy.ShardAccessStrategy);
  1576. }
  1577. /// <summary>
  1578. /// Create a new instance of <c>IQuery</c> for the given SQL string.
  1579. /// </summary>
  1580. /// <param name="sql">a query expressed in SQL</param>
  1581. /// <param name="returnAlias">a table alias that appears inside <c>{}</c> in the SQL string</param>
  1582. /// <param name="returnClass">the returned persistent class</param>
  1583. /// <returns>An <see cref="IQuery"/> from the SQL string</returns>
  1584. public IQuery CreateSQLQuery(string sql, string returnAlias, System.Type returnClass)
  1585. {
  1586. throw new NotSupportedException();
  1587. }
  1588. /// <summary>
  1589. /// Create a new instance of <see cref="IQuery" /> for the given SQL string.
  1590. /// </summary>
  1591. /// <param name="sql">a query expressed in SQL</param>
  1592. /// <param name="returnAliases">an array of table aliases that appear inside <c>{}</c> in the SQL string</param>
  1593. /// <param name="returnClasses">the returned persistent classes</param>
  1594. /// <returns>An <see cref="IQuery"/> from the SQL string</returns>
  1595. public IQuery CreateSQLQuery(string sql, string[] returnAliases, System.Type[] returnClasses)
  1596. {
  1597. throw new NotSupportedException();
  1598. }
  1599. /// <summary>
  1600. /// Create a new instance of <see cref="ISQLQuery" /> for the given SQL query string.
  1601. /// </summary>
  1602. /// <param name="queryString">a query expressed in SQL</param>
  1603. /// <returns>An <see cref="ISQLQuery"/> from the SQL string</returns>
  1604. public ISQLQuery CreateSQLQuery(string queryString)
  1605. {
  1606. throw new NotSupportedException();
  1607. }
  1608. /// <summary>
  1609. /// Completely clear the session. Evict all loaded instances and cancel all pending
  1610. /// saves, updates and deletions. Do not close open enumerables or instances of
  1611. /// <c>ScrollableResults</c>.
  1612. /// </summary>
  1613. public void Clear()
  1614. {
  1615. foreach (IShard shard in shards)
  1616. {
  1617. if(shard.Session != null)
  1618. {
  1619. shard.Session.Clear();
  1620. }
  1621. }
  1622. }
  1623. /// <summary>
  1624. /// Return the persistent instance of the given entity class with the given identifier, or null
  1625. /// if there is no such persistent instance. (If the instance, or a proxy for the instance, is
  1626. /// already associated with the session, return that instance or proxy.)
  1627. /// </summary>
  1628. /// <param name="clazz">a persistent class</param>
  1629. /// <param name="id">an identifier</param>
  1630. /// <returns>a persistent instance or null</returns>
  1631. public object Get(System.Type clazz, object id)
  1632. {
  1633. IShardOperation<Object> shardOp = new GetShardOperationByTypeAndId(clazz, id);
  1634. return ApplyGetOperation(shardOp, new ShardResolutionStrategyDataImpl(clazz, id));
  1635. }
  1636. /// <summary>
  1637. /// Return the persistent instance of the given entity class with the given identifier, or null
  1638. /// if there is no such persistent instance. Obtain the specified lock mode if the instance
  1639. /// exists.
  1640. /// </summary>
  1641. /// <param name="clazz">a persistent class</param>
  1642. /// <param name="id">an identifier</param>
  1643. /// <param name="lockMode">the lock mode</param>
  1644. /// <returns>a persistent instance or null</returns>
  1645. public object Get(System.Type clazz, object id, LockMode lockMode)
  1646. {
  1647. IShardOperation<object> shardOp = new GetShardOperationByTypeIdAndLockMode(clazz, (ISerializable) id, lockMode);
  1648. // we're not letting people customize shard selection by lockMode
  1649. return ApplyGetOperation(shardOp, new ShardResolutionStrategyDataImpl(clazz, id));
  1650. }
  1651. public object Get(string entityName, object id)
  1652. {
  1653. IShardOperation<object> shardOp = new GetShardOperationByEntityNameAndId(entityName, (ISerializable) id);
  1654. // we're not letting people customize shard selection by lockMode
  1655. return ApplyGetOperation(shardOp, new ShardResolutionStrategyDataImpl(entityName, id));
  1656. }
  1657. /// <summary>
  1658. /// Strongly-typed version of <see cref="ISession.Get(Type,object)" />
  1659. /// </summary>
  1660. public T Get<T>(object id)
  1661. {
  1662. return (T) this.Get(typeof (T), id);
  1663. }
  1664. /// <summary>
  1665. /// Strongly-typed version of <see cref="ISession.Get(Type,object,LockMode)" />
  1666. /// </summary>
  1667. public T Get<T>(object id, LockMode lockMode)
  1668. {
  1669. return (T) this.Get(typeof (T), id, lockMode);
  1670. }
  1671. /// <summary>
  1672. /// Return the entity name for a persistent entity
  1673. /// </summary>
  1674. /// <param name="obj">a persistent entity</param>
  1675. /// <returns> the entity name </returns>
  1676. public string GetEntityName(object obj)
  1677. {
  1678. IShardOperation<string> invoker = new GetShardOperationByEntityName(obj);
  1679. return InvokeOnShardWithObject(invoker, obj);
  1680. }
  1681. /**
  1682. * Helper method we can use when we need to find the Shard with which a
  1683. * specified object is associated and invoke the method on that Shard.
  1684. * If the object isn't associated with a Session we just invoke it on a
  1685. * random Session with the expectation that this will cause an error.
  1686. */
  1687. T InvokeOnShardWithObject<T>(IShardOperation<T> so, object obj )
  1688. {
  1689. ShardId shardId = GetShardIdForObject(obj);
  1690. // just ask this question of a random shard so we get the proper error
  1691. IShard shardToUse = shardId == null ? this.shards[0] : this.shardIdsToShards[shardId];
  1692. return so.Execute(shardToUse);
  1693. }
  1694. /// <summary>
  1695. /// Enable the named filter for this current session.
  1696. /// </summary>
  1697. /// <param name="filterName">The name of the filter to be enabled.</param>
  1698. /// <returns>The Filter instance representing the enabled fiter.</returns>
  1699. public IFilter EnableFilter(string filterName)
  1700. {
  1701. var filterEvent = new EnableFilterOpenSessionEvent(filterName);
  1702. foreach(IShard shard in shards)
  1703. {
  1704. if(shard.Session != null)
  1705. {
  1706. shard.Session.EnableFilter(filterName);
  1707. }
  1708. else
  1709. {
  1710. shard.AddOpenSessionEvent(filterEvent);
  1711. }
  1712. }
  1713. //TODO: what do we do here? A sharded filter?
  1714. return null;
  1715. }
  1716. /// <summary>
  1717. /// Retrieve a currently enabled filter by name.
  1718. /// </summary>
  1719. /// <param name="filterName">The name of the filter to be retrieved.</param>
  1720. /// <returns>The Filter instance representing the enabled fiter.</returns>
  1721. public IFilter GetEnabledFilter(string filterName)
  1722. {
  1723. // TODO(maxr) what do we return here? A sharded filter?
  1724. foreach(IShard shard in shards)
  1725. {
  1726. if(shard.Session != null)
  1727. {
  1728. IFilter filter = shard.Session.GetEnabledFilter(filterName);
  1729. if(filter != null)
  1730. {
  1731. return filter;
  1732. }
  1733. }
  1734. }
  1735. //TODO what do we do here?
  1736. return null;
  1737. }
  1738. /// <summary>
  1739. /// Disable the named filter for the current session.
  1740. /// </summary>
  1741. /// <param name="filterName">The name of the filter to be disabled.</param>
  1742. public void DisableFilter(string filterName)
  1743. {
  1744. var filterEvent = new DisableFilterOpenSessionEvent(filterName);
  1745. foreach(IShard shard in shards)
  1746. {
  1747. if (shard.Session != null)
  1748. {
  1749. shard.Session.DisableFilter(filterName);
  1750. }
  1751. else
  1752. {
  1753. shard.AddOpenSessionEvent(filterEvent);
  1754. }
  1755. }
  1756. }
  1757. /// <summary>
  1758. /// Create a multi query, a query that can send several
  1759. /// queries to the server, and return all their results in a single
  1760. /// call.
  1761. /// </summary>
  1762. /// <returns>
  1763. /// An <see cref="IMultiQuery"/> that can return
  1764. /// a list of all the results of all the queries.
  1765. /// Note that each query result is itself usually a list.
  1766. /// </returns>
  1767. public IMultiQuery CreateMultiQuery()
  1768. {
  1769. throw new NotSupportedException();
  1770. }
  1771. /// <summary>
  1772. /// Sets the batch size of the session
  1773. /// </summary>
  1774. /// <param name="batchSize"></param>
  1775. /// <returns></returns>
  1776. public ISession SetBatchSize(int batchSize)
  1777. {
  1778. throw new NotSupportedException();
  1779. }
  1780. /// <summary>
  1781. /// Gets the session implementation.
  1782. /// </summary>
  1783. /// <remarks>
  1784. /// This method is provided in order to get the <b>NHibernate</b> implementation of the session from wrapper implementions.
  1785. /// Implementors of the <seealso cref="ISession"/> interface should return the NHibernate implementation of this method.
  1786. /// </remarks>
  1787. /// <returns>
  1788. /// An NHibernate implementation of the <seealso cref="ISessionImplementor"/> interface
  1789. /// </returns>
  1790. public ISessionImplementor GetSessionImplementation()
  1791. {
  1792. throw new NotSupportedException();
  1793. }
  1794. /// <summary>
  1795. /// An <see cref="IMultiCriteria"/> that can return a list of all the results
  1796. /// of all the criterias.
  1797. /// </summary>
  1798. /// <returns></returns>
  1799. public IMultiCriteria CreateMultiCriteria()
  1800. {
  1801. throw new NotSupportedException();
  1802. }
  1803. public ISession GetSession(EntityMode entityMode)
  1804. {
  1805. throw new NotSupportedException();
  1806. }
  1807. public EntityMode ActiveEntityMode
  1808. {
  1809. get { throw new NotSupportedException(); }
  1810. }
  1811. /// <summary> Get the statistics for this session.</summary>
  1812. public ISessionStatistics Statistics
  1813. {
  1814. get { return new ShardedSessionStatistics(this); }
  1815. }
  1816. ///<summary>
  1817. ///Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
  1818. ///</summary>
  1819. ///<filterpriority>2</filterpriority>
  1820. public void Dispose()
  1821. {
  1822. if (!closed)
  1823. {
  1824. log.Warn("ShardedSessionImpl is being garbage collected but it was never properly closed.");
  1825. try
  1826. {
  1827. Close();
  1828. }
  1829. catch (Exception e)
  1830. {
  1831. log.Warn("Caught exception trying to close.", e);
  1832. }
  1833. }
  1834. }
  1835. #endregion
  1836. #region IShardedSessionImplementor Members
  1837. /// <summary>
  1838. /// Gets all the shards the ShardedSession is spanning.
  1839. /// Return list of all shards the ShardedSession is associated with
  1840. /// </summary>
  1841. public IList<IShard> Shards
  1842. {
  1843. get { return shards.AsReadOnly(); } //Collections.unmodifiableList(shards);
  1844. }
  1845. #endregion
  1846. private IList<ShardId> SelectShardIdsFromShardResolutionStrategyData(ShardResolutionStrategyDataImpl srsd)
  1847. {
  1848. IIdentifierGenerator idGenerator = shardedSessionFactory.GetIdentifierGenerator(srsd.EntityName);
  1849. if ((idGenerator is IShardEncodingIdentifierGenerator) && (srsd.Id != null))
  1850. {
  1851. return new[] { ((IShardEncodingIdentifierGenerator)idGenerator).ExtractShardId(srsd.Id) };
  1852. }
  1853. return shardStrategy.ShardResolutionStrategy.SelectShardIdsFromShardResolutionStrategyData(srsd);
  1854. }
  1855. private object ApplyGetOperation(IShardOperation<object> shardOp, ShardResolutionStrategyDataImpl srsd)
  1856. {
  1857. IList<ShardId> shardIds = SelectShardIdsFromShardResolutionStrategyData(srsd);
  1858. return shardStrategy.ShardAccessStrategy.Apply(ShardIdListToShardList(shardIds), shardOp,
  1859. new FirstNonNullResultExitStrategy<object>(),
  1860. new ExitOperationsQueryCollector());
  1861. }
  1862. private IList<IShard> ShardIdListToShardList(IEnumerable<ShardId> shardIds)
  1863. {
  1864. Set<IShard> shards = new HashedSet<IShard>();
  1865. foreach(ShardId shardId in shardIds)
  1866. {
  1867. shards.Add(shardIdsToShards[shardId]);
  1868. }
  1869. return shards.ToList();
  1870. }
  1871. private ISession GetSessionForObject(object obj, List<IShard> shardsToConsider)
  1872. {
  1873. IShard shard = GetShardForObject(obj, shardsToConsider);
  1874. if (shard == null)
  1875. {
  1876. return null;
  1877. }
  1878. return shard.Session;
  1879. }
  1880. private IShard GetShardForObject(object obj, IList<IShard> shardsToConsider)
  1881. {
  1882. foreach (IShard shard in shardsToConsider)
  1883. {
  1884. if (shard.Session != null && shard.Session.Contains(obj))
  1885. return shard;
  1886. }
  1887. return null;
  1888. }
  1889. internal ShardId GetShardIdForObject(object obj, List<IShard> shardsToConsider)
  1890. {
  1891. //TODO: Also, wouldn't it be faster to first see if there's just a single shard id mapped to the shard?
  1892. //TODO: optimize this by keeping an identity map of objects to shardId
  1893. IShard shard = GetShardForObject(obj, shardsToConsider);
  1894. if (shard == null)
  1895. {
  1896. return null;
  1897. }
  1898. if (shard.ShardIds.Count == 1)
  1899. {
  1900. IEnumerator<ShardId> iterator = shard.ShardIds.GetEnumerator();
  1901. iterator.MoveNext();
  1902. return iterator.Current;
  1903. }
  1904. string className;
  1905. if (obj is INHibernateProxy)
  1906. className = ((INHibernateProxy) obj).HibernateLazyInitializer.PersistentClass.Name;
  1907. else
  1908. className = obj.GetType().Name;
  1909. IIdentifierGenerator idGenerator = shard.SessionFactoryImplementor.GetIdentifierGenerator(className);
  1910. if (idGenerator is IShardEncodingIdentifierGenerator)
  1911. {
  1912. return ((IShardEncodingIdentifierGenerator)idGenerator).ExtractShardId(GetIdentifier(obj));
  1913. }
  1914. // TODO: also use shard resolution strategy if it returns only 1 shard; throw this error in config instead of here
  1915. throw new HibernateException("Can not use virtual sharding with non-shard resolving id gen");
  1916. }
  1917. private IDictionary<ShardId, IShard> BuildShardIdsToShardsMap()
  1918. {
  1919. var map = new Dictionary<ShardId, IShard>();
  1920. foreach (IShard shard in shards)
  1921. {
  1922. foreach (ShardId shardId in shard.ShardIds)
  1923. {
  1924. map.Add(shardId, shard);
  1925. }
  1926. }
  1927. return map;
  1928. }
  1929. private static List<IShard> BuildShardListFromSessionFactoryShardIdMap(
  1930. IDictionary<ISessionFactoryImplementor, Set<ShardId>> sessionFactoryShardIdMap,
  1931. bool checkAllAssociatedObjectsForDifferentShards,
  1932. IShardIdResolver shardIdResolver,
  1933. IInterceptor interceptor)
  1934. {
  1935. var shardList = new List<IShard>();
  1936. foreach (var entry in sessionFactoryShardIdMap)
  1937. {
  1938. IOpenSessionEvent eventToRegister = null;
  1939. IInterceptor interceptorToSet = interceptor;
  1940. if(checkAllAssociatedObjectsForDifferentShards)
  1941. {
  1942. // cross shard association checks for updates are handled using interceptors
  1943. var csrdi =
  1944. new CrossShardRelationshipDetectingInterceptor(shardIdResolver);
  1945. if(interceptorToSet == null)
  1946. {
  1947. // no interceptor to wrap so just use the cross-shard detecting interceptor raw
  1948. // this is safe because it's a stateless interceptor
  1949. interceptorToSet = csrdi;
  1950. }
  1951. else
  1952. {
  1953. // user specified their own interceptor, so wrap it with a decorator
  1954. // that will still do the cross shard association checks
  1955. Pair<IInterceptor, IOpenSessionEvent> result = DecorateInterceptor(csrdi, interceptor);
  1956. interceptorToSet = result.first;
  1957. eventToRegister = result.second;
  1958. }
  1959. }
  1960. else if(interceptorToSet != null)
  1961. {
  1962. // user specified their own interceptor so need to account for the fact
  1963. // that it might be stateful
  1964. Pair<IInterceptor, IOpenSessionEvent> result = HandleStatefulInterceptor(interceptorToSet);
  1965. interceptorToSet = result.first;
  1966. eventToRegister = result.second;
  1967. }
  1968. IShard shard = new ShardImpl(entry.Value,entry.Key,interceptorToSet);
  1969. shardList.Add(shard);
  1970. if(eventToRegister != null)
  1971. {
  1972. shard.AddOpenSessionEvent(eventToRegister);
  1973. }
  1974. }
  1975. return shardList;
  1976. }
  1977. internal static Pair<IInterceptor, IOpenSessionEvent> HandleStatefulInterceptor(IInterceptor mightBeStateful)
  1978. {
  1979. IOpenSessionEvent openSessionEvent = null;
  1980. if (mightBeStateful.GetType().GetInterfaces().Contains(typeof(IStatefulInterceptorFactory)))
  1981. {
  1982. mightBeStateful = ((IStatefulInterceptorFactory)mightBeStateful).NewInstance();
  1983. if (mightBeStateful.GetType().GetInterfaces().Contains(typeof(IRequiresSession)))
  1984. {
  1985. openSessionEvent = new SetSessionOnRequiresSessionEvent((IRequiresSession)mightBeStateful);
  1986. }
  1987. }
  1988. return Pair<IInterceptor, IOpenSessionEvent>.Of(mightBeStateful, openSessionEvent);
  1989. }
  1990. static Pair<IInterceptor,IOpenSessionEvent> DecorateInterceptor(CrossShardRelationshipDetectingInterceptor csrdi,IInterceptor decorateMe)
  1991. {
  1992. Pair<IInterceptor, IOpenSessionEvent> pair = HandleStatefulInterceptor(decorateMe);
  1993. IInterceptor decorator = new CrossShardRelationshipDetectingInterceptorDecorator(csrdi, pair.first);
  1994. return Pair<IInterceptor, IOpenSessionEvent>.Of(decorator, pair.second);
  1995. }
  1996. public object Get(System.Type clazz, ISerializable id)
  1997. {
  1998. var shardOp = new GetShardOperationByTypeAndId(clazz, id);
  1999. return ApplyGetOperation(shardOp, new ShardResolutionStrategyDataImpl(clazz, id));
  2000. }
  2001. public object Get(string entityName, ISerializable id)
  2002. {
  2003. var shardOp = new GetShardOperationByEntityNameAndId(entityName, id);
  2004. return ApplyGetOperation(shardOp, new ShardResolutionStrategyDataImpl(entityName, id));
  2005. }
  2006. private class SaveOrUpdateWithEntityName:ISaveOrUpdateOperation
  2007. {
  2008. private string entityName;
  2009. public SaveOrUpdateWithEntityName(string entityName)
  2010. {
  2011. this.entityName = entityName;
  2012. }
  2013. public void SaveOrUpdate(IShard shard, object obj)
  2014. {
  2015. shard.EstablishSession().SaveOrUpdate(entityName, obj);
  2016. }
  2017. public void Merge(IShard shard, object obj)
  2018. {
  2019. shard.EstablishSession().Merge(entityName, obj);
  2020. }
  2021. }
  2022. private class SaveOrUpdateSimple:ISaveOrUpdateOperation
  2023. {
  2024. public void SaveOrUpdate(IShard shard, object obj)
  2025. {
  2026. shard.EstablishSession().SaveOrUpdate(obj);
  2027. }
  2028. public void Merge(IShard shard, object obj)
  2029. {
  2030. shard.EstablishSession().Merge(obj);
  2031. }
  2032. }
  2033. private class UpdateOperationWithEntityName:IUpdateOperation
  2034. {
  2035. private string entityName;
  2036. public UpdateOperationWithEntityName(string entityName)
  2037. {
  2038. this.entityName = entityName;
  2039. }
  2040. public void Update(IShard shard, object obj)
  2041. {
  2042. shard.EstablishSession().Update(entityName, obj);
  2043. }
  2044. public void Merge(IShard shard, object obj)
  2045. {
  2046. shard.EstablishSession().Merge(entityName, obj);
  2047. }
  2048. }
  2049. private class UpdateOperationSimple:IUpdateOperation
  2050. {
  2051. public void Update(IShard shard, object obj)
  2052. {
  2053. shard.EstablishSession().Update(obj);
  2054. }
  2055. public void Merge(IShard shard, object obj)
  2056. {
  2057. shard.EstablishSession().Merge(obj);
  2058. }
  2059. }
  2060. private class DeleteOperationSimple:IDeleteOperation
  2061. {
  2062. public void Delete(IShard shard, object obj)
  2063. {
  2064. shard.EstablishSession().Delete(obj);
  2065. }
  2066. }
  2067. private class DeleteOperationWithEntityName:IDeleteOperation
  2068. {
  2069. private string entityName;
  2070. public DeleteOperationWithEntityName(string entityName)
  2071. {
  2072. this.entityName = entityName;
  2073. }
  2074. public void Delete(IShard shard, object obj)
  2075. {
  2076. shard.EstablishSession().Delete(entityName, obj);
  2077. }
  2078. }
  2079. private class RefreshOperationSimple:IRefreshOperation
  2080. {
  2081. public void Refresh(IShard shard, object obj)
  2082. {
  2083. shard.EstablishSession().Refresh(obj);
  2084. }
  2085. }
  2086. private class RefreshOperationWithLockMode:IRefreshOperation
  2087. {
  2088. private LockMode lockMode;
  2089. public RefreshOperationWithLockMode(LockMode lockMode)
  2090. {
  2091. this.lockMode = lockMode;
  2092. }
  2093. public void Refresh(IShard shard, object obj)
  2094. {
  2095. shard.EstablishSession().Refresh(obj, lockMode);
  2096. }
  2097. }
  2098. interface IRefreshOperation
  2099. {
  2100. void Refresh(IShard shard, object obj);
  2101. }
  2102. interface IDeleteOperation
  2103. {
  2104. void Delete(IShard shard, object obj);
  2105. }
  2106. interface IUpdateOperation
  2107. {
  2108. void Update(IShard shard, object obj);
  2109. void Merge(IShard shard, object obj);
  2110. }
  2111. interface ISaveOrUpdateOperation
  2112. {
  2113. void SaveOrUpdate(IShard shard, object obj);
  2114. void Merge(IShard shard, object obj);
  2115. }
  2116. #region Nested IShardOperation<object> types
  2117. private class GetShardOperationByTypeAndId : IShardOperation<object>
  2118. {
  2119. private readonly System.Type clazz;
  2120. private readonly object id;
  2121. public GetShardOperationByTypeAndId(System.Type clazz, object id)
  2122. {
  2123. this.clazz = clazz;
  2124. this.id = id;
  2125. }
  2126. #region IShardOperation<object> Members
  2127. public object Execute(IShard shard)
  2128. {
  2129. return shard.EstablishSession().Get(clazz, id);
  2130. }
  2131. public string OperationName
  2132. {
  2133. get { return "get(System.Type clazz, ISerializable id)"; }
  2134. }
  2135. #endregion
  2136. }
  2137. private class GetShardOperationByTypeIdAndLockMode : IShardOperation<object>
  2138. {
  2139. private readonly System.Type clazz;
  2140. private readonly ISerializable id;
  2141. private readonly LockMode lockMode;
  2142. public GetShardOperationByTypeIdAndLockMode(System.Type clazz, ISerializable id, LockMode lockMode)
  2143. {
  2144. this.clazz = clazz;
  2145. this.id = id;
  2146. this.lockMode = lockMode;
  2147. }
  2148. #region IShardOperation<object> Members
  2149. public object Execute(IShard shard)
  2150. {
  2151. return shard.EstablishSession().Get(clazz, id,lockMode);
  2152. }
  2153. public string OperationName
  2154. {
  2155. get { return "get(System.Type clazz, ISerializable id, LockMode lockMode)"; }
  2156. }
  2157. #endregion
  2158. }
  2159. private class GetShardOperationByEntityName:IShardOperation<string>
  2160. {
  2161. private object obj;
  2162. public GetShardOperationByEntityName(object obj)
  2163. {
  2164. this.obj = obj;
  2165. }
  2166. public string Execute(IShard shard)
  2167. {
  2168. return shard.EstablishSession().GetEntityName(obj);
  2169. }
  2170. public string OperationName
  2171. {
  2172. get{ return "getEntityName(object obj)"; }
  2173. }
  2174. }
  2175. private class GetShardOperationByEntityNameIdAndLockMode:IShardOperation<object>
  2176. {
  2177. private string entityName;
  2178. private ISerializable id;
  2179. private LockMode lockMode;
  2180. public GetShardOperationByEntityNameIdAndLockMode(string entityName, ISerializable id, LockMode lockMode)
  2181. {
  2182. this.entityName = entityName;
  2183. this.id = id;
  2184. this.lockMode = lockMode;
  2185. }
  2186. public object Execute(IShard shard)
  2187. {
  2188. return shard.EstablishSession().Load(entityName, id);
  2189. }
  2190. public string OperationName
  2191. {
  2192. get { return "get(string entityName, ISerializableid, LockMode lockMode)"; }
  2193. }
  2194. }
  2195. private class GetShardOperationByEntityNameAndId : IShardOperation<object>
  2196. {
  2197. private readonly string entityName;
  2198. private readonly ISerializable id;
  2199. public GetShardOperationByEntityNameAndId(string entityName, ISerializable id)
  2200. {
  2201. this.entityName = entityName;
  2202. this.id = id;
  2203. }
  2204. public object Execute(IShard shard)
  2205. {
  2206. return shard.EstablishSession().Get(entityName, id);
  2207. }
  2208. public string OperationName
  2209. {
  2210. get { return "get(string entityname, ISerializable id)"; }
  2211. }
  2212. }
  2213. private class LockShardOperationByEntityNameObjectLockMode:IShardOperation<object>
  2214. {
  2215. private string entityName;
  2216. private object obj;
  2217. private LockMode lockMode;
  2218. public LockShardOperationByEntityNameObjectLockMode(string entityName,object obj, LockMode lockMode)
  2219. {
  2220. this.entityName = entityName;
  2221. this.obj = obj;
  2222. this.lockMode = lockMode;
  2223. }
  2224. public object Execute(IShard shard)
  2225. {
  2226. shard.EstablishSession().Lock(entityName, obj, lockMode);
  2227. return null;
  2228. }
  2229. public string OperationName
  2230. {
  2231. get { return "Lock(string entityName, object obj, LockMode lockMode)"; }
  2232. }
  2233. }
  2234. private class LockShardOperationByObjectAndLockMode:IShardOperation<object>
  2235. {
  2236. private LockMode lockMode;
  2237. private object obj;
  2238. public LockShardOperationByObjectAndLockMode(object obj, LockMode lockMode)
  2239. {
  2240. this.lockMode = lockMode;
  2241. this.obj = obj;
  2242. }
  2243. public object Execute(IShard shard)
  2244. {
  2245. shard.EstablishSession().Lock(obj, lockMode);
  2246. return null;
  2247. }
  2248. public string OperationName
  2249. {
  2250. get { return "LockShardOperationByLockMode(object obj, LockMode lockMode)"; }
  2251. }
  2252. }
  2253. private class CurrentLockModeShardOperation:IShardOperation<LockMode>
  2254. {
  2255. private object obj;
  2256. public CurrentLockModeShardOperation(object obj)
  2257. {
  2258. this.obj = obj;
  2259. }
  2260. public LockMode Execute(IShard shard)
  2261. {
  2262. return shard.EstablishSession().GetCurrentLockMode(obj);
  2263. }
  2264. public string OperationName
  2265. {
  2266. get { return "GetCurrentLockMode(object obj)"; }
  2267. }
  2268. }
  2269. #endregion
  2270. }
  2271. }