Пример #1
0
        /// <summary> Delete any entities that were removed from the collection</summary>
        private async Task DeleteOrphansAsync(string entityName, IPersistentCollection pc, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            //TODO: suck this logic into the collection!
            ICollection orphans;

            if (pc.WasInitialized)
            {
                CollectionEntry ce = eventSource.PersistenceContext.GetCollectionEntry(pc);
                orphans = ce == null ? CollectionHelper.EmptyCollection : await(ce.GetOrphansAsync(entityName, pc, cancellationToken)).ConfigureAwait(false);
            }
            else
            {
                orphans = await(pc.GetQueuedOrphansAsync(entityName, cancellationToken)).ConfigureAwait(false);
            }

            foreach (object orphan in orphans)
            {
                if (orphan != null)
                {
                    log.Info("deleting orphaned entity instance: " + entityName);

                    await(eventSource.DeleteAsync(entityName, orphan, false, null, cancellationToken)).ConfigureAwait(false);
                }
            }
        }
Пример #2
0
        /// <summary> Delete any entities that were removed from the collection</summary>
        private void DeleteOrphans(string entityName, IPersistentCollection pc)
        {
            //TODO: suck this logic into the collection!
            ICollection orphans;

            if (pc.WasInitialized)
            {
                CollectionEntry ce = eventSource.PersistenceContext.GetCollectionEntry(pc);
                orphans = ce == null ? CollectionHelper.EmptyCollection : ce.GetOrphans(entityName, pc);
            }
            else
            {
                orphans = pc.GetQueuedOrphans(entityName);
            }

            foreach (object orphan in orphans)
            {
                if (orphan != null)
                {
                    log.Info("deleting orphaned entity instance: {0}", entityName);

                    eventSource.Delete(entityName, orphan, false, null);
                }
            }
        }
Пример #3
0
 /// <summary>
 /// After a collection was initialized or evicted, we don't
 /// need to batch fetch it anymore, remove it from the queue
 /// if necessary
 /// </summary>
 /// <param name="ce"></param>
 public void RemoveBatchLoadableCollection(CollectionEntry ce)
 {
     if (batchLoadableCollections.TryGetValue(ce.LoadedPersister.Role, out var map))
     {
         map.Remove(ce);
     }
 }
Пример #4
0
        private static void ProcessDereferencedCollection(IPersistentCollection coll, ISessionImplementor session)
        {
            IPersistenceContext  persistenceContext = session.PersistenceContext;
            CollectionEntry      entry           = persistenceContext.GetCollectionEntry(coll);
            ICollectionPersister loadedPersister = entry.LoadedPersister;

            if (log.IsDebugEnabled() && loadedPersister != null)
            {
                log.Debug("Collection dereferenced: {0}", MessageHelper.CollectionInfoString(loadedPersister, coll, entry.LoadedKey, session));
            }

            // do a check
            bool hasOrphanDelete = loadedPersister != null && loadedPersister.HasOrphanDelete;

            if (hasOrphanDelete)
            {
                object ownerId = loadedPersister.OwnerEntityPersister.GetIdentifier(coll.Owner);
                // TODO NH Different behavior
                //if (ownerId == null)
                //{
                //  // the owning entity may have been deleted and its identifier unset due to
                //  // identifier-rollback; in which case, try to look up its identifier from
                //  // the persistence context
                //  if (session.Factory.Settings.IsIdentifierRollbackEnabled)
                //  {
                //    EntityEntry ownerEntry = persistenceContext.GetEntry(coll.Owner);
                //    if (ownerEntry != null)
                //    {
                //      ownerId = ownerEntry.Id;
                //    }
                //  }
                //  if (ownerId == null)
                //  {
                //    throw new AssertionFailure("Unable to determine collection owner identifier for orphan-delete processing");
                //  }
                //}
                EntityKey key   = session.GenerateEntityKey(ownerId, loadedPersister.OwnerEntityPersister);
                object    owner = persistenceContext.GetEntity(key);
                if (owner == null)
                {
                    throw new AssertionFailure("collection owner not associated with session: " + loadedPersister.Role);
                }
                EntityEntry e = persistenceContext.GetEntry(owner);
                //only collections belonging to deleted entities are allowed to be dereferenced in the case of orphan delete
                if (e != null && e.Status != Status.Deleted && e.Status != Status.Gone)
                {
                    throw new HibernateException("A collection with cascade=\"all-delete-orphan\" was no longer referenced by the owning entity instance: " + loadedPersister.Role);
                }
            }

            // do the work
            entry.CurrentPersister = null;
            entry.CurrentKey       = null;
            PrepareCollectionForUpdate(coll, entry, session.Factory);
        }
Пример #5
0
        /// <summary>
        /// If a CollectionEntry represents a batch loadable collection, add
        /// it to the queue.
        /// </summary>
        /// <param name="collection"></param>
        /// <param name="ce"></param>
        public void AddBatchLoadableCollection(IPersistentCollection collection, CollectionEntry ce)
        {
            var persister = ce.LoadedPersister;

            if (!batchLoadableCollections.TryGetValue(persister.Role, out var map))
            {
                map = new LinkedHashMap <CollectionEntry, IPersistentCollection>();
                batchLoadableCollections.Add(persister.Role, map);
            }
            map[ce] = collection;
        }
Пример #6
0
        private static void ProcessNeverReferencedCollection(IPersistentCollection coll, ISessionImplementor session)
        {
            CollectionEntry entry = session.PersistenceContext.GetCollectionEntry(coll);

            log.Debug("Found collection with unloaded owner: {0}", MessageHelper.CollectionInfoString(entry.LoadedPersister, coll, entry.LoadedKey, session));

            entry.CurrentPersister = entry.LoadedPersister;
            entry.CurrentKey       = entry.LoadedKey;

            PrepareCollectionForUpdate(coll, entry, session.Factory);
        }
        public PersistentCollectionChangeWorkUnit(ISessionImplementor sessionImplementor, String entityName,
            AuditConfiguration auditCfg, IPersistentCollection collection,
            CollectionEntry collectionEntry, Object snapshot, Object id,
            String referencingPropertyName)
            : base(sessionImplementor, entityName, auditCfg, 
                    new PersistentCollectionChangeWorkUnitId(id, collectionEntry.Role))
        {
            this.ReferencingPropertyName = referencingPropertyName;

            collectionChanges = auditCfg.EntCfg[EntityName].PropertyMapper
                    .MapCollectionChanges(referencingPropertyName, collection, snapshot, id);
        }
        /// <summary>
        /// Links the created collection entry with the stored collection key.
        /// </summary>
        /// <param name="ce">The collection entry.</param>
        internal void LinkCollectionEntry(CollectionEntry ce)
        {
            if (!_queryCollectionKeys.TryGetValue(ce.LoadedPersister.Role, out var keys) ||
                keys.Count <= 0)
            {
                return;
            }
            var key = new CollectionKey(ce.LoadedPersister, ce.LoadedKey);

            if (keys.ContainsKey(key))
            {
                keys[key] = ce;
            }
        }
Пример #9
0
        /// <summary>
        /// Get a batch of uninitialized collection keys for a given role
        /// </summary>
        /// <param name="collectionPersister">The persister for the collection role.</param>
        /// <param name="id">A key that must be included in the batch fetch</param>
        /// <param name="batchSize">the maximum number of keys to return</param>
        /// <param name="cancellationToken">A cancellation token that can be used to cancel the work</param>
        /// <returns>an array of collection keys, of length batchSize (padded with nulls)</returns>
        public async Task <object[]> GetCollectionBatchAsync(ICollectionPersister collectionPersister, object id, int batchSize, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            object[] keys = new object[batchSize];
            keys[0] = id;
            int  i           = 1;
            int  end         = -1;
            bool checkForEnd = false;

            // this only works because collection entries are kept in a sequenced
            // map by persistence context (maybe we should do like entities and
            // keep a separate sequences set...)
            foreach (DictionaryEntry me in context.CollectionEntries)
            {
                CollectionEntry       ce         = (CollectionEntry)me.Value;
                IPersistentCollection collection = (IPersistentCollection)me.Key;
                if (!collection.WasInitialized && ce.LoadedPersister == collectionPersister)
                {
                    if (checkForEnd && i == end)
                    {
                        return(keys);                        //the first key found after the given key
                    }

                    //if ( end == -1 && count > batchSize*10 ) return keys; //try out ten batches, max

                    bool isEqual = collectionPersister.KeyType.IsEqual(id, ce.LoadedKey, collectionPersister.Factory);

                    if (isEqual)
                    {
                        end = i;
                        //checkForEnd = false;
                    }
                    else if (!await(IsCachedAsync(ce.LoadedKey, collectionPersister, cancellationToken)).ConfigureAwait(false))
                    {
                        keys[i++] = ce.LoadedKey;
                        //count++;
                    }

                    if (i == batchSize)
                    {
                        i = 1;                         //end of array, start filling again from start
                        if (end != -1)
                        {
                            checkForEnd = true;
                        }
                    }
                }
            }
            return(keys);            //we ran out of keys to try
        }
Пример #10
0
        /// <summary>
        /// Initialize the role of the collection.
        /// </summary>
        /// <param name="collection">The collection to be updated by reachability. </param>
        /// <param name="type">The type of the collection. </param>
        /// <param name="entity">The owner of the collection. </param>
        /// <param name="session">The session.</param>
        /// <param name="cancellationToken">A cancellation token that can be used to cancel the work</param>
        public static Task ProcessReachableCollectionAsync(IPersistentCollection collection, CollectionType type, object entity, ISessionImplementor session, CancellationToken cancellationToken)
        {
            if (cancellationToken.IsCancellationRequested)
            {
                return(Task.FromCanceled <object>(cancellationToken));
            }
            try
            {
                collection.Owner = entity;
                CollectionEntry ce = session.PersistenceContext.GetCollectionEntry(collection);

                if (ce == null)
                {
                    // refer to comment in StatefulPersistenceContext.addCollection()
                    return(Task.FromException <object>(new HibernateException(string.Format("Found two representations of same collection: {0}", type.Role))));
                }

                // The CollectionEntry.isReached() stuff is just to detect any silly users
                // who set up circular or shared references between/to collections.
                if (ce.IsReached)
                {
                    // We've been here before
                    return(Task.FromException <object>(new HibernateException(string.Format("Found shared references to a collection: {0}", type.Role))));
                }
                ce.IsReached = true;

                ISessionFactoryImplementor factory   = session.Factory;
                ICollectionPersister       persister = factory.GetCollectionPersister(type.Role);
                ce.CurrentPersister = persister;
                ce.CurrentKey       = type.GetKeyOfOwner(entity, session);           //TODO: better to pass the id in as an argument?

                if (log.IsDebugEnabled)
                {
                    log.Debug("Collection found: " +
                              MessageHelper.CollectionInfoString(persister, collection, ce.CurrentKey, session) + ", was: " +
                              MessageHelper.CollectionInfoString(ce.LoadedPersister, collection, ce.LoadedKey, session) +
                              (collection.WasInitialized ? " (initialized)" : " (uninitialized)"));
                }

                return(PrepareCollectionForUpdateAsync(collection, ce, factory, cancellationToken));
            }
            catch (System.Exception ex)
            {
                return(Task.FromException <object>(ex));
            }
        }
Пример #11
0
        private static Task ProcessNeverReferencedCollectionAsync(IPersistentCollection coll, ISessionImplementor session, CancellationToken cancellationToken)
        {
            if (cancellationToken.IsCancellationRequested)
            {
                return(Task.FromCanceled <object>(cancellationToken));
            }
            try
            {
                CollectionEntry entry = session.PersistenceContext.GetCollectionEntry(coll);

                log.Debug("Found collection with unloaded owner: " + MessageHelper.CollectionInfoString(entry.LoadedPersister, coll, entry.LoadedKey, session));

                entry.CurrentPersister = entry.LoadedPersister;
                entry.CurrentKey       = entry.LoadedKey;

                return(PrepareCollectionForUpdateAsync(coll, entry, session.Factory, cancellationToken));
            }
            catch (System.Exception ex)
            {
                return(Task.FromException <object>(ex));
            }
        }
Пример #12
0
        /// <summary>
        /// Initialize the role of the collection.
        /// </summary>
        /// <param name="collection">The collection to be updated by reachability. </param>
        /// <param name="type">The type of the collection. </param>
        /// <param name="entity">The owner of the collection. </param>
        /// <param name="session">The session.</param>
        public static void ProcessReachableCollection(IPersistentCollection collection, CollectionType type, object entity, ISessionImplementor session)
        {
            collection.Owner = entity;
            CollectionEntry ce = session.PersistenceContext.GetCollectionEntry(collection);

            if (ce == null)
            {
                // refer to comment in StatefulPersistenceContext.addCollection()
                throw new HibernateException(string.Format("Found two representations of same collection: {0}", type.Role));
            }

            // The CollectionEntry.isReached() stuff is just to detect any silly users
            // who set up circular or shared references between/to collections.
            if (ce.IsReached)
            {
                // We've been here before
                throw new HibernateException(string.Format("Found shared references to a collection: {0}", type.Role));
            }
            ce.IsReached = true;

            ISessionFactoryImplementor factory   = session.Factory;
            ICollectionPersister       persister = factory.GetCollectionPersister(type.Role);

            ce.CurrentPersister = persister;
            ce.CurrentKey       = type.GetKeyOfOwner(entity, session);       //TODO: better to pass the id in as an argument?

            if (log.IsDebugEnabled())
            {
                log.Debug("Collection found: {0}, was: {1}{2}",
                          MessageHelper.CollectionInfoString(persister, collection, ce.CurrentKey, session),
                          MessageHelper.CollectionInfoString(ce.LoadedPersister, collection, ce.LoadedKey, session),
                          (collection.WasInitialized ? " (initialized)" : " (uninitialized)"));
            }

            PrepareCollectionForUpdate(collection, ce, factory);
        }
 /// <summary>
 /// Checks whether the collection entry was already checked in the cache.
 /// </summary>
 /// <param name="persister">The collection persister.</param>
 /// <param name="entry">The collection entry.</param>
 /// <returns><see langword="true"/> whether the collection entry was checked, <see langword="false"/> otherwise.</returns>
 internal bool WasCollectionEntryChecked(ICollectionPersister persister, CollectionEntry entry)
 {
     return(_queryCheckedCollectionEntries.TryGetValue(persister.Role, out var checkedEntries) &&
            checkedEntries.Contains(entry));
 }
Пример #14
0
 /// <summary>
 /// Retrives the uninitialized persistent collection from the queue.
 /// </summary>
 /// <param name="persister">The collection persister.</param>
 /// <param name="ce">The collection entry.</param>
 /// <returns>A persistent collection if found, <see langword="null"/> otherwise.</returns>
 internal IPersistentCollection GetBatchLoadableCollection(ICollectionPersister persister, CollectionEntry ce)
 {
     if (!batchLoadableCollections.TryGetValue(persister.Role, out var map))
     {
         return(null);
     }
     if (!map.TryGetValue(ce, out var collection))
     {
         return(null);
     }
     return(collection);
 }
Пример #15
0
        private static void PrepareCollectionForUpdate(IPersistentCollection collection, CollectionEntry entry, ISessionFactoryImplementor factory)
        {
            //1. record the collection role that this collection is referenced by
            //2. decide if the collection needs deleting/creating/updating (but don't actually schedule the action yet)

            if (entry.IsProcessed)
            {
                throw new AssertionFailure("collection was processed twice by flush()");
            }

            entry.IsProcessed = true;

            ICollectionPersister loadedPersister  = entry.LoadedPersister;
            ICollectionPersister currentPersister = entry.CurrentPersister;

            if (loadedPersister != null || currentPersister != null)
            {
                // it is or was referenced _somewhere_
                bool ownerChanged = loadedPersister != currentPersister ||
                                    !currentPersister.KeyType.IsEqual(entry.LoadedKey, entry.CurrentKey, factory);

                if (ownerChanged)
                {
                    // do a check
                    bool orphanDeleteAndRoleChanged = loadedPersister != null &&
                                                      currentPersister != null && loadedPersister.HasOrphanDelete;

                    if (orphanDeleteAndRoleChanged)
                    {
                        throw new HibernateException("Don't change the reference to a collection with cascade=\"all-delete-orphan\": " + loadedPersister.Role);
                    }

                    // do the work
                    if (currentPersister != null)
                    {
                        entry.IsDorecreate = true;                         // we will need to create new entries
                    }

                    if (loadedPersister != null)
                    {
                        entry.IsDoremove = true;                         // we will need to remove ye olde entries
                        if (entry.IsDorecreate)
                        {
                            log.Debug("Forcing collection initialization");
                            collection.ForceInitialization();                             // force initialize!
                        }
                    }
                }
                else if (collection.IsDirty)
                {
                    // else if it's elements changed
                    entry.IsDoupdate = true;
                }
            }
        }
        /// <summary>
        /// Get a batch of all uninitialized collection keys for a given role that are present in the cached query.
        /// Once this method is called the uninitialized collection keys for a given role will be cleared in order to prevent
        /// double checking the same keys.
        /// </summary>
        /// <param name="collectionPersister">The persister for the collection role.</param>
        /// <param name="key">A key that must be included in the batch fetch.</param>
        /// <param name="collectionEntries">An array that will be filled with collection entries if set.</param>
        /// <returns>
        /// An array of collection keys that can be empty if the key was already checked or <see langword="null" />
        /// if the key is not present in the cached query.
        /// </returns>
        internal object[] GetCollectionBatch(ICollectionPersister collectionPersister, object key, out CollectionEntry[] collectionEntries)
        {
            if (!_queryCollectionKeys.TryGetValue(collectionPersister.Role, out var keys))
            {
                collectionEntries = null;
                return(null);                // The collection was not present in the cached query
            }

            var collectionKey = new CollectionKey(collectionPersister, key);

            if (_queryCheckedCollectionKeys.TryGetValue(collectionPersister.Role, out var checkedKeys) &&
                checkedKeys.Contains(collectionKey))
            {
                collectionEntries = null;
                return(Array.Empty <object>());
            }

            if (!keys.TryGetValue(collectionKey, out var collectionEntry) || collectionEntry == null)
            {
                collectionEntries = null;
                return(null);                // The collection was not present in the cached query
            }

            if (checkedKeys == null)
            {
                checkedKeys = new HashSet <CollectionKey>();
                _queryCheckedCollectionKeys.Add(collectionPersister.Role, checkedKeys);
            }

            if (!_queryCheckedCollectionEntries.TryGetValue(collectionPersister.Role, out var checkedEntries))
            {
                checkedEntries = new HashSet <CollectionEntry>();
                _queryCheckedCollectionEntries.Add(collectionPersister.Role, checkedEntries);
            }

            var result = new object[keys.Count];

            collectionEntries = new CollectionEntry[result.Length];
            var i = 0;

            result[i++] = key;

            foreach (var pair in keys)
            {
                if (pair.Value == null || _persistenceContext.GetCollection(pair.Key)?.WasInitialized != false)
                {
                    continue;                     // The collection was not registered or is already initialized
                }

                if (collectionPersister.KeyType.IsEqual(key, pair.Value.LoadedKey, collectionPersister.Factory))
                {
                    collectionEntries[0] = pair.Value;
                    checkedKeys.Add(pair.Key);
                    checkedEntries.Add(pair.Value);
                    continue;
                }

                collectionEntries[i] = pair.Value;
                result[i++]          = pair.Value.LoadedKey;
                checkedKeys.Add(pair.Key);
                checkedEntries.Add(pair.Value);
            }

            keys.Clear();

            return(result);
        }
Пример #17
0
        private void OnCollectionAction(AbstractCollectionEvent evt,
										IPersistentCollection newColl,
										object oldColl,
										CollectionEntry collectionEntry)
        {
            if (!VerCfg.GlobalCfg.GenerateRevisionsForCollections)
                return;
            var entityName = evt.GetAffectedOwnerEntityName();
            if (!VerCfg.EntCfg.IsVersioned(entityName)) return;

            var verSync = VerCfg.AuditProcessManager.Get(evt.Session);
            var ownerEntityName = ((AbstractCollectionPersister)collectionEntry.LoadedPersister).OwnerEntityName;
            var referencingPropertyName = collectionEntry.Role.Substring(ownerEntityName.Length + 1);

            // Checking if this is not a "fake" many-to-one bidirectional relation. The relation description may be
            // null in case of collections of non-entities.
            var rd = VerCfg.EntCfg[entityName].GetRelationDescription(referencingPropertyName);
            if (rd != null && rd.MappedByPropertyName != null)
            {
                GenerateFakeBidirecationalRelationWorkUnits(verSync, newColl, oldColl, entityName,
                                                            referencingPropertyName, evt, rd);
            }
            else
            {
                var workUnit = new PersistentCollectionChangeWorkUnit(evt.Session, entityName, VerCfg, newColl,
                                                                collectionEntry, oldColl, evt.AffectedOwnerIdOrNull,
                                                                referencingPropertyName);
                verSync.AddWorkUnit(workUnit);

                if (workUnit.ContainsWork())
                {
                    // There are some changes: a revision needs also be generated for the collection owner
                    verSync.AddWorkUnit(new CollectionChangeWorkUnit(evt.Session, evt.GetAffectedOwnerEntityName(),
                                                                VerCfg, evt.AffectedOwnerIdOrNull, evt.AffectedOwnerOrNull));

                    GenerateBidirectionalCollectionChangeWorkUnits(verSync, evt, workUnit, rd);
                }
            }
        }