1 /*
2 * Hibernate, Relational Persistence for Idiomatic Java
3 *
4 * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
5 * indicated by the @author tags or express copyright attribution
6 * statements applied by the authors. All third-party contributions are
7 * distributed under license by Red Hat Middleware LLC.
8 *
9 * This copyrighted material is made available to anyone wishing to use, modify,
10 * copy, or redistribute it subject to the terms and conditions of the GNU
11 * Lesser General Public License, as published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
16 * for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this distribution; if not, write to:
20 * Free Software Foundation, Inc.
21 * 51 Franklin Street, Fifth Floor
22 * Boston, MA 02110-1301 USA
23 *
24 */
25 package org.hibernate.engine;
26
27 import java.io.Serializable;
28
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
31 import org.hibernate.AssertionFailure;
32 import org.hibernate.CacheMode;
33 import org.hibernate.HibernateException;
34 import org.hibernate.LockMode;
35 import org.hibernate.cache.CacheKey;
36 import org.hibernate.cache.entry.CacheEntry;
37 import org.hibernate.event.PostLoadEvent;
38 import org.hibernate.event.PostLoadEventListener;
39 import org.hibernate.event.PreLoadEvent;
40 import org.hibernate.event.PreLoadEventListener;
41 import org.hibernate.intercept.LazyPropertyInitializer;
42 import org.hibernate.persister.entity.EntityPersister;
43 import org.hibernate.pretty.MessageHelper;
44 import org.hibernate.property.BackrefPropertyAccessor;
45 import org.hibernate.type.Type;
46 import org.hibernate.type.TypeFactory;
47
48 /**
49 * Functionality relating to Hibernate's two-phase loading process,
50 * that may be reused by persisters that do not use the Loader
51 * framework
52 *
53 * @author Gavin King
54 */
55 public final class TwoPhaseLoad {
56
57 private static final Logger log = LoggerFactory.getLogger(TwoPhaseLoad.class);
58
59 private TwoPhaseLoad() {}
60
61 /**
62 * Register the "hydrated" state of an entity instance, after the first step of 2-phase loading.
63 *
64 * Add the "hydrated state" (an array) of an uninitialized entity to the session. We don't try
65 * to resolve any associations yet, because there might be other entities waiting to be
66 * read from the JDBC result set we are currently processing
67 */
68 public static void postHydrate(
69 final EntityPersister persister,
70 final Serializable id,
71 final Object[] values,
72 final Object rowId,
73 final Object object,
74 final LockMode lockMode,
75 final boolean lazyPropertiesAreUnfetched,
76 final SessionImplementor session)
77 throws HibernateException {
78
79 Object version = Versioning.getVersion(values, persister);
80 session.getPersistenceContext().addEntry(
81 object,
82 Status.LOADING,
83 values,
84 rowId,
85 id,
86 version,
87 lockMode,
88 true,
89 persister,
90 false,
91 lazyPropertiesAreUnfetched
92 );
93
94 if ( log.isTraceEnabled() && version!=null ) {
95 String versionStr = persister.isVersioned()
96 ? persister.getVersionType().toLoggableString( version, session.getFactory() )
97 : "null";
98 log.trace( "Version: " + versionStr );
99 }
100
101 }
102
103 /**
104 * Perform the second step of 2-phase load. Fully initialize the entity
105 * instance.
106 *
107 * After processing a JDBC result set, we "resolve" all the associations
108 * between the entities which were instantiated and had their state
109 * "hydrated" into an array
110 */
111 public static void initializeEntity(
112 final Object entity,
113 final boolean readOnly,
114 final SessionImplementor session,
115 final PreLoadEvent preLoadEvent,
116 final PostLoadEvent postLoadEvent) throws HibernateException {
117
118 //TODO: Should this be an InitializeEntityEventListener??? (watch out for performance!)
119
120 final PersistenceContext persistenceContext = session.getPersistenceContext();
121 EntityEntry entityEntry = persistenceContext.getEntry(entity);
122 if ( entityEntry == null ) {
123 throw new AssertionFailure( "possible non-threadsafe access to the session" );
124 }
125 EntityPersister persister = entityEntry.getPersister();
126 Serializable id = entityEntry.getId();
127 Object[] hydratedState = entityEntry.getLoadedState();
128
129 if ( log.isDebugEnabled() )
130 log.debug(
131 "resolving associations for " +
132 MessageHelper.infoString(persister, id, session.getFactory())
133 );
134
135 Type[] types = persister.getPropertyTypes();
136 for ( int i = 0; i < hydratedState.length; i++ ) {
137 final Object value = hydratedState[i];
138 if ( value!=LazyPropertyInitializer.UNFETCHED_PROPERTY && value!=BackrefPropertyAccessor.UNKNOWN ) {
139 hydratedState[i] = types[i].resolve( value, session, entity );
140 }
141 }
142
143 //Must occur after resolving identifiers!
144 if ( session.isEventSource() ) {
145 preLoadEvent.setEntity(entity).setState(hydratedState).setId(id).setPersister(persister);
146 PreLoadEventListener[] listeners = session.getListeners().getPreLoadEventListeners();
147 for ( int i = 0; i < listeners.length; i++ ) {
148 listeners[i].onPreLoad(preLoadEvent);
149 }
150 }
151
152 persister.setPropertyValues( entity, hydratedState, session.getEntityMode() );
153
154 final SessionFactoryImplementor factory = session.getFactory();
155 if ( persister.hasCache() && session.getCacheMode().isPutEnabled() ) {
156
157 if ( log.isDebugEnabled() )
158 log.debug(
159 "adding entity to second-level cache: " +
160 MessageHelper.infoString( persister, id, session.getFactory() )
161 );
162
163 Object version = Versioning.getVersion(hydratedState, persister);
164 CacheEntry entry = new CacheEntry(
165 hydratedState,
166 persister,
167 entityEntry.isLoadedWithLazyPropertiesUnfetched(),
168 version,
169 session,
170 entity
171 );
172 CacheKey cacheKey = new CacheKey(
173 id,
174 persister.getIdentifierType(),
175 persister.getRootEntityName(),
176 session.getEntityMode(),
177 session.getFactory()
178 );
179 boolean put = persister.getCacheAccessStrategy().putFromLoad(
180 cacheKey,
181 persister.getCacheEntryStructure().structure( entry ),
182 session.getTimestamp(),
183 version,
184 useMinimalPuts( session, entityEntry )
185 );
186
187 if ( put && factory.getStatistics().isStatisticsEnabled() ) {
188 factory.getStatisticsImplementor().secondLevelCachePut( persister.getCacheAccessStrategy().getRegion().getName() );
189 }
190 }
191
192 if ( readOnly || !persister.isMutable() ) {
193 //no need to take a snapshot - this is a
194 //performance optimization, but not really
195 //important, except for entities with huge
196 //mutable property values
197 persistenceContext.setEntryStatus(entityEntry, Status.READ_ONLY);
198 }
199 else {
200 //take a snapshot
201 TypeFactory.deepCopy(
202 hydratedState,
203 persister.getPropertyTypes(),
204 persister.getPropertyUpdateability(),
205 hydratedState, //after setting values to object, entityMode
206 session
207 );
208 persistenceContext.setEntryStatus(entityEntry, Status.MANAGED);
209 }
210
211 persister.afterInitialize(
212 entity,
213 entityEntry.isLoadedWithLazyPropertiesUnfetched(),
214 session
215 );
216
217 if ( session.isEventSource() ) {
218 postLoadEvent.setEntity(entity).setId(id).setPersister(persister);
219 PostLoadEventListener[] listeners = session.getListeners().getPostLoadEventListeners();
220 for ( int i = 0; i < listeners.length; i++ ) {
221 listeners[i].onPostLoad(postLoadEvent);
222 }
223 }
224
225 if ( log.isDebugEnabled() )
226 log.debug(
227 "done materializing entity " +
228 MessageHelper.infoString( persister, id, session.getFactory() )
229 );
230
231 if ( factory.getStatistics().isStatisticsEnabled() ) {
232 factory.getStatisticsImplementor().loadEntity( persister.getEntityName() );
233 }
234
235 }
236
237 private static boolean useMinimalPuts(SessionImplementor session, EntityEntry entityEntry) {
238 return ( session.getFactory().getSettings().isMinimalPutsEnabled() &&
239 session.getCacheMode()!=CacheMode.REFRESH ) ||
240 ( entityEntry.getPersister().hasLazyProperties() &&
241 entityEntry.isLoadedWithLazyPropertiesUnfetched() &&
242 entityEntry.getPersister().isLazyPropertiesCacheable() );
243 }
244
245 /**
246 * Add an uninitialized instance of an entity class, as a placeholder to ensure object
247 * identity. Must be called before <tt>postHydrate()</tt>.
248 *
249 * Create a "temporary" entry for a newly instantiated entity. The entity is uninitialized,
250 * but we need the mapping from id to instance in order to guarantee uniqueness.
251 */
252 public static void addUninitializedEntity(
253 final EntityKey key,
254 final Object object,
255 final EntityPersister persister,
256 final LockMode lockMode,
257 final boolean lazyPropertiesAreUnfetched,
258 final SessionImplementor session
259 ) {
260 session.getPersistenceContext().addEntity(
261 object,
262 Status.LOADING,
263 null,
264 key,
265 null,
266 lockMode,
267 true,
268 persister,
269 false,
270 lazyPropertiesAreUnfetched
271 );
272 }
273
274 public static void addUninitializedCachedEntity(
275 final EntityKey key,
276 final Object object,
277 final EntityPersister persister,
278 final LockMode lockMode,
279 final boolean lazyPropertiesAreUnfetched,
280 final Object version,
281 final SessionImplementor session
282 ) {
283 session.getPersistenceContext().addEntity(
284 object,
285 Status.LOADING,
286 null,
287 key,
288 version,
289 lockMode,
290 true,
291 persister,
292 false,
293 lazyPropertiesAreUnfetched
294 );
295 }
296 }