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 import java.io.ObjectOutputStream;
29 import java.io.IOException;
30 import java.io.ObjectInputStream;
31
32
33 import org.hibernate.EntityMode;
34 import org.hibernate.HibernateException;
35 import org.hibernate.LockMode;
36 import org.hibernate.intercept.FieldInterceptionHelper;
37 import org.hibernate.persister.entity.EntityPersister;
38 import org.hibernate.persister.entity.UniqueKeyLoadable;
39 import org.hibernate.pretty.MessageHelper;
40
41 /**
42 * We need an entry to tell us all about the current state
43 * of an object with respect to its persistent state
44 *
45 * @author Gavin King
46 */
47 public final class EntityEntry implements Serializable {
48
49 private LockMode lockMode;
50 private Status status;
51 private final Serializable id;
52 private Object[] loadedState;
53 private Object[] deletedState;
54 private boolean existsInDatabase;
55 private Object version;
56 private transient EntityPersister persister; // for convenience to save some lookups
57 private final EntityMode entityMode;
58 private final String entityName;
59 private boolean isBeingReplicated;
60 private boolean loadedWithLazyPropertiesUnfetched; //NOTE: this is not updated when properties are fetched lazily!
61 private final transient Object rowId;
62
63 EntityEntry(
64 final Status status,
65 final Object[] loadedState,
66 final Object rowId,
67 final Serializable id,
68 final Object version,
69 final LockMode lockMode,
70 final boolean existsInDatabase,
71 final EntityPersister persister,
72 final EntityMode entityMode,
73 final boolean disableVersionIncrement,
74 final boolean lazyPropertiesAreUnfetched) {
75 this.status=status;
76 this.loadedState=loadedState;
77 this.id=id;
78 this.rowId=rowId;
79 this.existsInDatabase=existsInDatabase;
80 this.version=version;
81 this.lockMode=lockMode;
82 this.isBeingReplicated=disableVersionIncrement;
83 this.loadedWithLazyPropertiesUnfetched = lazyPropertiesAreUnfetched;
84 this.persister=persister;
85 this.entityMode = entityMode;
86 this.entityName = persister == null ?
87 null : persister.getEntityName();
88 }
89
90 /**
91 * Used during custom deserialization
92 */
93 private EntityEntry(
94 final SessionFactoryImplementor factory,
95 final String entityName,
96 final Serializable id,
97 final EntityMode entityMode,
98 final Status status,
99 final Object[] loadedState,
100 final Object[] deletedState,
101 final Object version,
102 final LockMode lockMode,
103 final boolean existsInDatabase,
104 final boolean isBeingReplicated,
105 final boolean loadedWithLazyPropertiesUnfetched) {
106 this.entityName = entityName;
107 this.persister = factory.getEntityPersister( entityName );
108 this.id = id;
109 this.entityMode = entityMode;
110 this.status = status;
111 this.loadedState = loadedState;
112 this.deletedState = deletedState;
113 this.version = version;
114 this.lockMode = lockMode;
115 this.existsInDatabase = existsInDatabase;
116 this.isBeingReplicated = isBeingReplicated;
117 this.loadedWithLazyPropertiesUnfetched = loadedWithLazyPropertiesUnfetched;
118 this.rowId = null; // this is equivalent to the old behavior...
119 }
120
121 public LockMode getLockMode() {
122 return lockMode;
123 }
124
125 public void setLockMode(LockMode lockMode) {
126 this.lockMode = lockMode;
127 }
128
129 public Status getStatus() {
130 return status;
131 }
132
133 public void setStatus(Status status) {
134 if (status==Status.READ_ONLY) {
135 loadedState = null; //memory optimization
136 }
137 this.status = status;
138 }
139
140 public Serializable getId() {
141 return id;
142 }
143
144 public Object[] getLoadedState() {
145 return loadedState;
146 }
147
148 public Object[] getDeletedState() {
149 return deletedState;
150 }
151
152 public void setDeletedState(Object[] deletedState) {
153 this.deletedState = deletedState;
154 }
155
156 public boolean isExistsInDatabase() {
157 return existsInDatabase;
158 }
159
160 public Object getVersion() {
161 return version;
162 }
163
164 public EntityPersister getPersister() {
165 return persister;
166 }
167
168 void afterDeserialize(SessionFactoryImplementor factory) {
169 persister = factory.getEntityPersister( entityName );
170 }
171
172 public String getEntityName() {
173 return entityName;
174 }
175
176 public boolean isBeingReplicated() {
177 return isBeingReplicated;
178 }
179
180 public Object getRowId() {
181 return rowId;
182 }
183
184 /**
185 * After actually updating the database, update the snapshot information,
186 * and escalate the lock mode
187 */
188 public void postUpdate(Object entity, Object[] updatedState, Object nextVersion) {
189 this.loadedState = updatedState;
190
191 setLockMode(LockMode.WRITE);
192
193 if ( getPersister().isVersioned() ) {
194 this.version = nextVersion;
195 getPersister().setPropertyValue(
196 entity,
197 getPersister().getVersionProperty(),
198 nextVersion,
199 entityMode
200 );
201 }
202
203 FieldInterceptionHelper.clearDirty( entity );
204 }
205
206 /**
207 * After actually deleting a row, record the fact that the instance no longer
208 * exists in the database
209 */
210 public void postDelete() {
211 status = Status.GONE;
212 existsInDatabase = false;
213 }
214
215 /**
216 * After actually inserting a row, record the fact that the instance exists on the
217 * database (needed for identity-column key generation)
218 */
219 public void postInsert() {
220 existsInDatabase = true;
221 }
222
223 public boolean isNullifiable(boolean earlyInsert, SessionImplementor session) {
224 return getStatus() == Status.SAVING || (
225 earlyInsert ?
226 !isExistsInDatabase() :
227 session.getPersistenceContext().getNullifiableEntityKeys()
228 .contains( new EntityKey( getId(), getPersister(), entityMode ) )
229 );
230 }
231
232 public Object getLoadedValue(String propertyName) {
233 int propertyIndex = ( (UniqueKeyLoadable) persister ).getPropertyIndex(propertyName);
234 return loadedState[propertyIndex];
235 }
236
237
238 public boolean requiresDirtyCheck(Object entity) {
239
240 boolean isMutableInstance =
241 status != Status.READ_ONLY &&
242 persister.isMutable();
243
244 return isMutableInstance && (
245 getPersister().hasMutableProperties() ||
246 !FieldInterceptionHelper.isInstrumented( entity ) ||
247 FieldInterceptionHelper.extractFieldInterceptor( entity).isDirty()
248 );
249
250 }
251
252 public void forceLocked(Object entity, Object nextVersion) {
253 version = nextVersion;
254 loadedState[ persister.getVersionProperty() ] = version;
255 setLockMode( LockMode.FORCE );
256 persister.setPropertyValue(
257 entity,
258 getPersister().getVersionProperty(),
259 nextVersion,
260 entityMode
261 );
262 }
263
264 public void setReadOnly(boolean readOnly, Object entity) {
265 if (status!=Status.MANAGED && status!=Status.READ_ONLY) {
266 throw new HibernateException("instance was not in a valid state");
267 }
268 if (readOnly) {
269 setStatus(Status.READ_ONLY);
270 loadedState = null;
271 }
272 else {
273 setStatus(Status.MANAGED);
274 loadedState = getPersister().getPropertyValues(entity, entityMode);
275 }
276 }
277
278 public String toString() {
279 return "EntityEntry" +
280 MessageHelper.infoString(entityName, id) +
281 '(' + status + ')';
282 }
283
284 public boolean isLoadedWithLazyPropertiesUnfetched() {
285 return loadedWithLazyPropertiesUnfetched;
286 }
287
288
289 /**
290 * Custom serialization routine used during serialization of a
291 * Session/PersistenceContext for increased performance.
292 *
293 * @param oos The stream to which we should write the serial data.
294 * @throws java.io.IOException
295 */
296 void serialize(ObjectOutputStream oos) throws IOException {
297 oos.writeObject( entityName );
298 oos.writeObject( id );
299 oos.writeObject( entityMode.toString() );
300 oos.writeObject( status.toString() );
301 // todo : potentially look at optimizing these two arrays
302 oos.writeObject( loadedState );
303 oos.writeObject( deletedState );
304 oos.writeObject( version );
305 oos.writeObject( lockMode.toString() );
306 oos.writeBoolean( existsInDatabase );
307 oos.writeBoolean( isBeingReplicated );
308 oos.writeBoolean( loadedWithLazyPropertiesUnfetched );
309 }
310
311 /**
312 * Custom deserialization routine used during deserialization of a
313 * Session/PersistenceContext for increased performance.
314 *
315 * @param ois The stream from which to read the entry.
316 * @param session The session being deserialized.
317 * @return The deserialized EntityEntry
318 * @throws IOException
319 * @throws ClassNotFoundException
320 */
321 static EntityEntry deserialize(
322 ObjectInputStream ois,
323 SessionImplementor session) throws IOException, ClassNotFoundException {
324 return new EntityEntry(
325 session.getFactory(),
326 ( String ) ois.readObject(),
327 ( Serializable ) ois.readObject(),
328 EntityMode.parse( ( String ) ois.readObject() ),
329 Status.parse( ( String ) ois.readObject() ),
330 ( Object[] ) ois.readObject(),
331 ( Object[] ) ois.readObject(),
332 ( Object ) ois.readObject(),
333 LockMode.parse( ( String ) ois.readObject() ),
334 ois.readBoolean(),
335 ois.readBoolean(),
336 ois.readBoolean()
337 );
338 }
339 }