forked from nhibernate/nhibernate-core
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathTwoPhaseLoad.cs
199 lines (174 loc) · 7.81 KB
/
TwoPhaseLoad.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
using System.Diagnostics;
using NHibernate.Cache;
using NHibernate.Cache.Entry;
using NHibernate.Event;
using NHibernate.Impl;
using NHibernate.Intercept;
using NHibernate.Persister.Entity;
using NHibernate.Proxy;
using NHibernate.Type;
using NHibernate.Properties;
namespace NHibernate.Engine
{
/// <summary>
/// Functionality relating to Hibernate's two-phase loading process,
/// that may be reused by persisters that do not use the Loader
/// framework
/// </summary>
public static class TwoPhaseLoad
{
private static readonly IInternalLogger log = LoggerProvider.LoggerFor(typeof(TwoPhaseLoad));
/// <summary>
/// Register the "hydrated" state of an entity instance, after the first step of 2-phase loading.
///
/// Add the "hydrated state" (an array) of an uninitialized entity to the session. We don't try
/// to resolve any associations yet, because there might be other entities waiting to be
/// read from the JDBC result set we are currently processing
/// </summary>
public static void PostHydrate(IEntityPersister persister, object id, object[] values, object rowId, object obj, LockMode lockMode, bool lazyPropertiesAreUnfetched, ISessionImplementor session)
{
object version = Versioning.GetVersion(values, persister);
session.PersistenceContext.AddEntry(obj, Status.Loading, values, rowId, id, version, lockMode, true, persister, false, lazyPropertiesAreUnfetched);
if (log.IsDebugEnabled && version != null)
{
System.String versionStr = persister.IsVersioned ? persister.VersionType.ToLoggableString(version, session.Factory) : "null";
log.Debug("Version: " + versionStr);
}
}
/// <summary>
/// Perform the second step of 2-phase load. Fully initialize the entity instance.
/// After processing a JDBC result set, we "resolve" all the associations
/// between the entities which were instantiated and had their state
/// "hydrated" into an array
/// </summary>
public static void InitializeEntity(object entity, bool readOnly, ISessionImplementor session, PreLoadEvent preLoadEvent, PostLoadEvent postLoadEvent)
{
//TODO: Should this be an InitializeEntityEventListener??? (watch out for performance!)
bool statsEnabled = session.Factory.Statistics.IsStatisticsEnabled;
var stopWath = new Stopwatch();
if (statsEnabled)
{
stopWath.Start();
}
IPersistenceContext persistenceContext = session.PersistenceContext;
EntityEntry entityEntry = persistenceContext.GetEntry(entity);
if (entityEntry == null)
{
throw new AssertionFailure("possible non-threadsafe access to the session");
}
IEntityPersister persister = entityEntry.Persister;
object id = entityEntry.Id;
object[] hydratedState = entityEntry.LoadedState;
if (log.IsDebugEnabled)
log.Debug("resolving associations for " + MessageHelper.InfoString(persister, id, session.Factory));
IType[] types = persister.PropertyTypes;
for (int i = 0; i < hydratedState.Length; i++)
{
object value = hydratedState[i];
if (value != LazyPropertyInitializer.UnfetchedProperty && value != BackrefPropertyAccessor.Unknown)
{
hydratedState[i] = types[i].ResolveIdentifier(value, session, entity);
}
}
//Must occur after resolving identifiers!
if (session.IsEventSource)
{
preLoadEvent.Entity = entity;
preLoadEvent.State = hydratedState;
preLoadEvent.Id = id;
preLoadEvent.Persister=persister;
IPreLoadEventListener[] listeners = session.Listeners.PreLoadEventListeners;
for (int i = 0; i < listeners.Length; i++)
{
listeners[i].OnPreLoad(preLoadEvent);
}
}
persister.SetPropertyValues(entity, hydratedState, session.EntityMode);
ISessionFactoryImplementor factory = session.Factory;
if (persister.HasCache && ((session.CacheMode & CacheMode.Put) == CacheMode.Put))
{
if (log.IsDebugEnabled)
log.Debug("adding entity to second-level cache: " + MessageHelper.InfoString(persister, id, session.Factory));
object version = Versioning.GetVersion(hydratedState, persister);
CacheEntry entry =
new CacheEntry(hydratedState, persister, entityEntry.LoadedWithLazyPropertiesUnfetched, version, session, entity);
CacheKey cacheKey = new CacheKey(id, persister.IdentifierType, persister.RootEntityName, session.EntityMode, session.Factory);
bool put =
persister.Cache.Put(cacheKey, persister.CacheEntryStructure.Structure(entry), session.Timestamp, version,
persister.IsVersioned ? persister.VersionType.Comparator : null,
UseMinimalPuts(session, entityEntry));
if (put && factory.Statistics.IsStatisticsEnabled)
{
factory.StatisticsImplementor.SecondLevelCachePut(persister.Cache.RegionName);
}
}
bool isReallyReadOnly = readOnly;
if (!persister.IsMutable)
{
isReallyReadOnly = true;
}
else
{
object proxy = persistenceContext.GetProxy(entityEntry.EntityKey);
if (proxy != null)
{
// there is already a proxy for this impl
// only set the status to read-only if the proxy is read-only
isReallyReadOnly = ((INHibernateProxy)proxy).HibernateLazyInitializer.ReadOnly;
}
}
if (isReallyReadOnly)
{
//no need to take a snapshot - this is a
//performance optimization, but not really
//important, except for entities with huge
//mutable property values
persistenceContext.SetEntryStatus(entityEntry, Status.ReadOnly);
}
else
{
//take a snapshot
TypeHelper.DeepCopy(hydratedState, persister.PropertyTypes, persister.PropertyUpdateability, hydratedState, session);
persistenceContext.SetEntryStatus(entityEntry, Status.Loaded);
}
persister.AfterInitialize(entity, entityEntry.LoadedWithLazyPropertiesUnfetched, session);
if (session.IsEventSource)
{
postLoadEvent.Entity = entity;
postLoadEvent.Id = id;
postLoadEvent.Persister = persister;
IPostLoadEventListener[] listeners = session.Listeners.PostLoadEventListeners;
for (int i = 0; i < listeners.Length; i++)
{
listeners[i].OnPostLoad(postLoadEvent);
}
}
if (log.IsDebugEnabled)
log.Debug("done materializing entity " + MessageHelper.InfoString(persister, id, session.Factory));
if (statsEnabled)
{
stopWath.Stop();
factory.StatisticsImplementor.LoadEntity(persister.EntityName, stopWath.Elapsed);
}
}
private static bool UseMinimalPuts(ISessionImplementor session, EntityEntry entityEntry)
{
return (session.Factory.Settings.IsMinimalPutsEnabled && session.CacheMode != CacheMode.Refresh)
|| (entityEntry.Persister.HasLazyProperties && entityEntry.LoadedWithLazyPropertiesUnfetched && entityEntry.Persister.IsLazyPropertiesCacheable);
}
/// <summary>
/// Add an uninitialized instance of an entity class, as a placeholder to ensure object
/// identity. Must be called before <tt>postHydrate()</tt>.
/// Create a "temporary" entry for a newly instantiated entity. The entity is uninitialized,
/// but we need the mapping from id to instance in order to guarantee uniqueness.
/// </summary>
public static void AddUninitializedEntity(EntityKey key, object obj, IEntityPersister persister, LockMode lockMode, bool lazyPropertiesAreUnfetched, ISessionImplementor session)
{
session.PersistenceContext.AddEntity(obj, Status.Loading, null, key, null, lockMode, true, persister, false, lazyPropertiesAreUnfetched);
}
public static void AddUninitializedCachedEntity(EntityKey key, object obj, IEntityPersister persister, LockMode lockMode, bool lazyPropertiesAreUnfetched, object version, ISessionImplementor session)
{
session.PersistenceContext.AddEntity(obj, Status.Loading, null, key, version, lockMode, true, persister, false, lazyPropertiesAreUnfetched);
}
}
}