Revision 68144e34
Added by Andreas Müller over 3 years ago
eu.etaxonomy.taxeditor.cdmlib/src/main/java/org/hibernate/collection/internal/AbstractPersistentCollection.java | ||
---|---|---|
34 | 34 |
import java.util.Iterator; |
35 | 35 |
import java.util.List; |
36 | 36 |
import java.util.ListIterator; |
37 |
import java.util.Map; |
|
38 | 37 |
|
39 | 38 |
import javax.naming.NamingException; |
40 | 39 |
|
41 | 40 |
import org.hibernate.AssertionFailure; |
42 |
import org.hibernate.FlushMode; |
|
43 | 41 |
import org.hibernate.HibernateException; |
44 | 42 |
import org.hibernate.LazyInitializationException; |
45 | 43 |
import org.hibernate.Session; |
46 |
import org.hibernate.collection.internal.AbstractPersistentCollection.DelayedOperation; |
|
47 |
import org.hibernate.collection.internal.AbstractPersistentCollection.IteratorProxy; |
|
48 |
import org.hibernate.collection.internal.AbstractPersistentCollection.LazyInitializationWork; |
|
49 |
import org.hibernate.collection.internal.AbstractPersistentCollection.ListIteratorProxy; |
|
50 |
import org.hibernate.collection.internal.AbstractPersistentCollection.ValueDelayedOperation; |
|
51 | 44 |
import org.hibernate.collection.spi.PersistentCollection; |
52 | 45 |
import org.hibernate.engine.internal.ForeignKeys; |
53 | 46 |
import org.hibernate.engine.spi.CollectionEntry; |
... | ... | |
56 | 49 |
import org.hibernate.engine.spi.SessionImplementor; |
57 | 50 |
import org.hibernate.engine.spi.Status; |
58 | 51 |
import org.hibernate.engine.spi.TypedValue; |
59 |
import org.hibernate.internal.CoreLogging; |
|
60 |
import org.hibernate.internal.CoreMessageLogger; |
|
61 | 52 |
import org.hibernate.internal.SessionFactoryRegistry; |
62 | 53 |
import org.hibernate.internal.util.MarkerObject; |
63 | 54 |
import org.hibernate.internal.util.collections.EmptyIterator; |
... | ... | |
65 | 56 |
import org.hibernate.persister.collection.CollectionPersister; |
66 | 57 |
import org.hibernate.persister.entity.EntityPersister; |
67 | 58 |
import org.hibernate.pretty.MessageHelper; |
68 |
import org.hibernate.type.CompositeType; |
|
69 |
import org.hibernate.type.IntegerType; |
|
70 |
import org.hibernate.type.LongType; |
|
71 |
import org.hibernate.type.PostgresUUIDType; |
|
72 |
import org.hibernate.type.StringType; |
|
73 | 59 |
import org.hibernate.type.Type; |
74 |
import org.hibernate.type.UUIDBinaryType; |
|
75 |
import org.hibernate.type.UUIDCharType; |
|
76 | 60 |
import org.jboss.logging.Logger; |
77 | 61 |
|
78 | 62 |
import eu.etaxonomy.cdm.api.application.CdmApplicationRemoteConfiguration; |
... | ... | |
92 | 76 |
* @author Cherian Mathew |
93 | 77 |
*/ |
94 | 78 |
public abstract class AbstractPersistentCollection implements Serializable, PersistentCollection { |
95 |
|
|
79 |
private static final Logger log = Logger.getLogger( AbstractPersistentCollection.class ); |
|
80 |
|
|
96 | 81 |
/** |
97 | 82 |
* <b>IMPORTANT:</b><br> |
98 | 83 |
* This serialVersionUID must be kept in sync with the serialVersionUID which is generated |
... | ... | |
113 | 98 |
*/ |
114 | 99 |
private static final long serialVersionUID = 7094296207968006972L; |
115 | 100 |
|
116 |
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( AbstractPersistentCollection.class ); |
|
117 |
|
|
118 | 101 |
private transient SessionImplementor session; |
119 |
private boolean isTempSession = false; |
|
120 | 102 |
private boolean initialized; |
121 | 103 |
private transient List<DelayedOperation> operationQueue; |
122 | 104 |
private transient boolean directlyAccessible; |
... | ... | |
132 | 114 |
private Serializable storedSnapshot; |
133 | 115 |
|
134 | 116 |
private String sessionFactoryUuid; |
135 |
private boolean allowLoadOutsideTransaction; |
|
136 |
|
|
137 |
/** |
|
138 |
* Not called by Hibernate, but used by non-JDK serialization, |
|
139 |
* eg. SOAP libraries. |
|
140 |
*/ |
|
141 |
public AbstractPersistentCollection() { |
|
142 |
} |
|
143 |
|
|
144 |
protected AbstractPersistentCollection(SessionImplementor session) { |
|
145 |
this.session = session; |
|
146 |
} |
|
117 |
private boolean specjLazyLoad = false; |
|
147 | 118 |
|
148 | 119 |
@Override |
149 |
public final String getRole() {
|
|
120 |
public final String getRole() {
|
|
150 | 121 |
return role; |
151 | 122 |
} |
152 | 123 |
|
153 | 124 |
@Override |
154 |
public final Serializable getKey() {
|
|
125 |
public final Serializable getKey() {
|
|
155 | 126 |
return key; |
156 | 127 |
} |
157 | 128 |
|
158 | 129 |
@Override |
159 |
public final boolean isUnreferenced() {
|
|
130 |
public final boolean isUnreferenced() {
|
|
160 | 131 |
return role == null; |
161 | 132 |
} |
162 | 133 |
|
163 | 134 |
@Override |
164 |
public final boolean isDirty() {
|
|
135 |
public final boolean isDirty() {
|
|
165 | 136 |
return dirty; |
166 | 137 |
} |
167 | 138 |
|
168 | 139 |
@Override |
169 |
public final void clearDirty() {
|
|
140 |
public final void clearDirty() {
|
|
170 | 141 |
dirty = false; |
171 | 142 |
} |
172 | 143 |
|
173 | 144 |
@Override |
174 |
public final void dirty() {
|
|
145 |
public final void dirty() {
|
|
175 | 146 |
dirty = true; |
176 | 147 |
} |
177 | 148 |
|
178 | 149 |
@Override |
179 |
public final Serializable getStoredSnapshot() {
|
|
150 |
public final Serializable getStoredSnapshot() {
|
|
180 | 151 |
return storedSnapshot; |
181 | 152 |
} |
182 | 153 |
|
183 | 154 |
//Careful: these methods do not initialize the collection. |
184 | 155 |
|
156 |
/** |
|
157 |
* Is the initialized collection empty? |
|
158 |
*/ |
|
185 | 159 |
@Override |
186 |
public abstract boolean empty();
|
|
160 |
public abstract boolean empty();
|
|
187 | 161 |
|
188 | 162 |
/** |
189 | 163 |
* Called by any read-only method of the collection interface |
... | ... | |
202 | 176 |
return true; |
203 | 177 |
} |
204 | 178 |
else { |
205 |
final boolean isExtraLazy = withTemporarySessionIfNeeded( |
|
206 |
new LazyInitializationWork<Boolean>() { |
|
207 |
@Override |
|
208 |
public Boolean doWork() { |
|
209 |
final CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this ); |
|
210 |
|
|
211 |
if ( entry != null ) { |
|
212 |
final CollectionPersister persister = entry.getLoadedPersister(); |
|
213 |
if ( persister.isExtraLazy() ) { |
|
214 |
if ( hasQueuedOperations() ) { |
|
215 |
session.flush(); |
|
179 |
// In remoting we are sure that session is null |
|
180 |
// both when using property paths and switching off conversations |
|
181 |
if(session == null && remoting) { |
|
182 |
log.info("--> readSize, of " + getRole() + " with key " + getKey()); |
|
183 |
read(); |
|
184 |
} else { |
|
185 |
boolean isExtraLazy = withTemporarySessionIfNeeded( |
|
186 |
new LazyInitializationWork<Boolean>() { |
|
187 |
@Override |
|
188 |
public Boolean doWork() { |
|
189 |
CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this ); |
|
190 |
|
|
191 |
if ( entry != null ) { |
|
192 |
CollectionPersister persister = entry.getLoadedPersister(); |
|
193 |
if ( persister.isExtraLazy() ) { |
|
194 |
if ( hasQueuedOperations() ) { |
|
195 |
session.flush(); |
|
196 |
} |
|
197 |
cachedSize = persister.getSize( entry.getLoadedKey(), session ); |
|
198 |
return true; |
|
199 |
} |
|
200 |
else { |
|
201 |
read(); |
|
216 | 202 |
} |
217 |
cachedSize = persister.getSize( entry.getLoadedKey(), session ); |
|
218 |
return true; |
|
219 | 203 |
} |
220 |
else {
|
|
221 |
read();
|
|
204 |
else{ |
|
205 |
throwLazyInitializationExceptionIfNotConnected();
|
|
222 | 206 |
} |
207 |
return false; |
|
223 | 208 |
} |
224 |
else{ |
|
225 |
throwLazyInitializationExceptionIfNotConnected(); |
|
226 |
} |
|
227 |
return false; |
|
228 | 209 |
} |
229 |
}
|
|
230 |
);
|
|
231 |
if ( isExtraLazy ) {
|
|
232 |
return true;
|
|
210 |
);
|
|
211 |
if ( isExtraLazy ) {
|
|
212 |
return true;
|
|
213 |
}
|
|
233 | 214 |
} |
234 | 215 |
} |
235 | 216 |
} |
236 | 217 |
return false; |
237 | 218 |
} |
238 | 219 |
|
239 |
/** |
|
240 |
* TBH not sure why this is public |
|
241 |
* |
|
242 |
* @param <T> The java type of the return for this LazyInitializationWork |
|
243 |
*/ |
|
244 | 220 |
public static interface LazyInitializationWork<T> { |
245 |
/** |
|
246 |
* Do the represented work and return the result. |
|
247 |
* |
|
248 |
* @return The result |
|
249 |
*/ |
|
250 | 221 |
public T doWork(); |
251 | 222 |
} |
252 | 223 |
|
253 | 224 |
private <T> T withTemporarySessionIfNeeded(LazyInitializationWork<T> lazyInitializationWork) { |
254 |
SessionImplementor tempSession = null; |
|
225 |
SessionImplementor originalSession = null; |
|
226 |
boolean isTempSession = false; |
|
227 |
boolean isJTA = false; |
|
255 | 228 |
|
256 | 229 |
if ( session == null ) { |
257 |
if ( allowLoadOutsideTransaction ) { |
|
258 |
tempSession = openTemporarySessionForLoading(); |
|
230 |
if ( specjLazyLoad ) { |
|
231 |
session = openTemporarySessionForLoading(); |
|
232 |
isTempSession = true; |
|
259 | 233 |
} |
260 | 234 |
else { |
261 | 235 |
throwLazyInitializationException( "could not initialize proxy - no Session" ); |
262 | 236 |
} |
263 | 237 |
} |
264 | 238 |
else if ( !session.isOpen() ) { |
265 |
if ( allowLoadOutsideTransaction ) { |
|
266 |
tempSession = openTemporarySessionForLoading(); |
|
239 |
if ( specjLazyLoad ) { |
|
240 |
originalSession = session; |
|
241 |
session = openTemporarySessionForLoading(); |
|
242 |
isTempSession = true; |
|
267 | 243 |
} |
268 | 244 |
else { |
269 | 245 |
throwLazyInitializationException( "could not initialize proxy - the owning Session was closed" ); |
270 | 246 |
} |
271 | 247 |
} |
272 | 248 |
else if ( !session.isConnected() ) { |
273 |
if ( allowLoadOutsideTransaction ) { |
|
274 |
tempSession = openTemporarySessionForLoading(); |
|
249 |
if ( specjLazyLoad ) { |
|
250 |
originalSession = session; |
|
251 |
session = openTemporarySessionForLoading(); |
|
252 |
isTempSession = true; |
|
275 | 253 |
} |
276 | 254 |
else { |
277 | 255 |
throwLazyInitializationException( "could not initialize proxy - the owning Session is disconnected" ); |
278 | 256 |
} |
279 | 257 |
} |
280 | 258 |
|
281 |
|
|
282 |
SessionImplementor originalSession = null; |
|
283 |
boolean isJTA = false; |
|
284 |
|
|
285 |
if ( tempSession != null ) { |
|
286 |
isTempSession = true; |
|
287 |
originalSession = session; |
|
288 |
session = tempSession; |
|
289 |
|
|
290 |
|
|
259 |
if ( isTempSession ) { |
|
260 |
// TODO: On the next major release, add an |
|
261 |
// 'isJTA' or 'getTransactionFactory' method to Session. |
|
262 |
/*isJTA = session.getTransactionCoordinator() |
|
263 |
.getTransactionContext().getTransactionEnvironment() |
|
264 |
.getTransactionFactory() |
|
265 |
.compatibleWithJtaSynchronization();*/ |
|
291 | 266 |
isJTA = session.getTransactionCoordinator().getTransactionCoordinatorBuilder().isJta(); |
292 |
|
|
293 | 267 |
if ( !isJTA ) { |
294 | 268 |
// Explicitly handle the transactions only if we're not in |
295 | 269 |
// a JTA environment. A lazy loading temporary session can |
296 | 270 |
// be created even if a current session and transaction are |
297 | 271 |
// open (ex: session.clear() was used). We must prevent |
298 | 272 |
// multiple transactions. |
299 |
( (Session) session ).beginTransaction(); |
|
273 |
( ( Session) session ).beginTransaction();
|
|
300 | 274 |
} |
301 | 275 |
|
302 | 276 |
session.getPersistenceContext().addUninitializedDetachedCollection( |
... | ... | |
309 | 283 |
return lazyInitializationWork.doWork(); |
310 | 284 |
} |
311 | 285 |
finally { |
312 |
if ( tempSession != null ) {
|
|
286 |
if ( isTempSession ) {
|
|
313 | 287 |
// make sure the just opened temp session gets closed! |
314 |
isTempSession = false; |
|
315 |
session = originalSession; |
|
316 |
|
|
317 | 288 |
try { |
318 | 289 |
if ( !isJTA ) { |
319 |
( (Session) tempSession ).getTransaction().commit();
|
|
290 |
( ( Session) session ).getTransaction().commit();
|
|
320 | 291 |
} |
321 |
( (Session) tempSession ).close();
|
|
292 |
( (Session) session ).close();
|
|
322 | 293 |
} |
323 | 294 |
catch (Exception e) { |
324 |
LOG.warn( "Unable to close temporary session used to load lazy collection associated to no session" );
|
|
295 |
log.warn( "Unable to close temporary session used to load lazy collection associated to no session" );
|
|
325 | 296 |
} |
297 |
session = originalSession; |
|
326 | 298 |
} |
327 | 299 |
} |
328 | 300 |
} |
... | ... | |
332 | 304 |
throwLazyInitializationException( "SessionFactory UUID not known to create temporary Session for loading" ); |
333 | 305 |
} |
334 | 306 |
|
335 |
final SessionFactoryImplementor sf = (SessionFactoryImplementor)
|
|
307 |
SessionFactoryImplementor sf = (SessionFactoryImplementor) |
|
336 | 308 |
SessionFactoryRegistry.INSTANCE.getSessionFactory( sessionFactoryUuid ); |
337 |
final SessionImplementor session = (SessionImplementor) sf.openSession(); |
|
338 |
session.getPersistenceContext().setDefaultReadOnly( true ); |
|
339 |
session.setFlushMode( FlushMode.MANUAL ); |
|
340 |
return session; |
|
309 |
return (SessionImplementor) sf.openSession(); |
|
341 | 310 |
} |
342 | 311 |
|
343 | 312 |
protected Boolean readIndexExistence(final Object index) { |
344 | 313 |
if ( !initialized ) { |
345 |
final Boolean extraLazyExistenceCheck = withTemporarySessionIfNeeded( |
|
346 |
new LazyInitializationWork<Boolean>() { |
|
347 |
@Override |
|
348 |
public Boolean doWork() { |
|
349 |
final CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this ); |
|
350 |
final CollectionPersister persister = entry.getLoadedPersister(); |
|
351 |
if ( persister.isExtraLazy() ) { |
|
352 |
if ( hasQueuedOperations() ) { |
|
353 |
session.flush(); |
|
314 |
// In remoting we are sure that session is null |
|
315 |
// both when using property paths and switching off conversations |
|
316 |
if(session == null && remoting) { |
|
317 |
log.info("--> readIndexExistence, of " + getRole() + " with key " + getKey()); |
|
318 |
read(); |
|
319 |
} else { |
|
320 |
Boolean extraLazyExistenceCheck = withTemporarySessionIfNeeded( |
|
321 |
new LazyInitializationWork<Boolean>() { |
|
322 |
@Override |
|
323 |
public Boolean doWork() { |
|
324 |
CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this ); |
|
325 |
CollectionPersister persister = entry.getLoadedPersister(); |
|
326 |
if ( persister.isExtraLazy() ) { |
|
327 |
if ( hasQueuedOperations() ) { |
|
328 |
session.flush(); |
|
329 |
} |
|
330 |
return persister.indexExists( entry.getLoadedKey(), index, session ); |
|
354 | 331 |
} |
355 |
return persister.indexExists( entry.getLoadedKey(), index, session );
|
|
356 |
}
|
|
357 |
else {
|
|
358 |
read();
|
|
332 |
else {
|
|
333 |
read();
|
|
334 |
}
|
|
335 |
return null;
|
|
359 | 336 |
} |
360 |
return null; |
|
361 | 337 |
} |
362 |
}
|
|
363 |
);
|
|
364 |
if ( extraLazyExistenceCheck != null ) {
|
|
365 |
return extraLazyExistenceCheck;
|
|
338 |
);
|
|
339 |
if ( extraLazyExistenceCheck != null ) {
|
|
340 |
return extraLazyExistenceCheck;
|
|
341 |
}
|
|
366 | 342 |
} |
367 | 343 |
} |
368 | 344 |
return null; |
... | ... | |
370 | 346 |
|
371 | 347 |
protected Boolean readElementExistence(final Object element) { |
372 | 348 |
if ( !initialized ) { |
373 |
final Boolean extraLazyExistenceCheck = withTemporarySessionIfNeeded( |
|
374 |
new LazyInitializationWork<Boolean>() { |
|
375 |
@Override |
|
376 |
public Boolean doWork() { |
|
377 |
final CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this ); |
|
378 |
final CollectionPersister persister = entry.getLoadedPersister(); |
|
379 |
if ( persister.isExtraLazy() ) { |
|
380 |
if ( hasQueuedOperations() ) { |
|
381 |
session.flush(); |
|
349 |
// In remoting we are sure that session is null |
|
350 |
// both when using property paths and switching off conversations |
|
351 |
if(session == null && remoting) { |
|
352 |
log.info("--> readElementExistence, of " + getRole() + " with key " + getKey()); |
|
353 |
read(); |
|
354 |
|
|
355 |
} else { |
|
356 |
Boolean extraLazyExistenceCheck = withTemporarySessionIfNeeded( |
|
357 |
new LazyInitializationWork<Boolean>() { |
|
358 |
@Override |
|
359 |
public Boolean doWork() { |
|
360 |
CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this ); |
|
361 |
CollectionPersister persister = entry.getLoadedPersister(); |
|
362 |
if ( persister.isExtraLazy() ) { |
|
363 |
if ( hasQueuedOperations() ) { |
|
364 |
session.flush(); |
|
365 |
} |
|
366 |
return persister.elementExists( entry.getLoadedKey(), element, session ); |
|
382 | 367 |
} |
383 |
return persister.elementExists( entry.getLoadedKey(), element, session );
|
|
384 |
}
|
|
385 |
else {
|
|
386 |
read();
|
|
368 |
else {
|
|
369 |
read();
|
|
370 |
}
|
|
371 |
return null;
|
|
387 | 372 |
} |
388 |
return null; |
|
389 | 373 |
} |
390 |
}
|
|
391 |
);
|
|
392 |
if ( extraLazyExistenceCheck != null ) {
|
|
393 |
return extraLazyExistenceCheck;
|
|
374 |
);
|
|
375 |
if ( extraLazyExistenceCheck != null ) {
|
|
376 |
return extraLazyExistenceCheck;
|
|
377 |
}
|
|
394 | 378 |
} |
395 | 379 |
} |
396 | 380 |
return null; |
... | ... | |
400 | 384 |
|
401 | 385 |
protected Object readElementByIndex(final Object index) { |
402 | 386 |
if ( !initialized ) { |
403 |
class ExtraLazyElementByIndexReader implements LazyInitializationWork { |
|
404 |
private boolean isExtraLazy; |
|
405 |
private Object element; |
|
387 |
// In remoting we are sure that session is null |
|
388 |
// both when using property paths and switching off conversations |
|
389 |
if(session == null && remoting) { |
|
390 |
log.info("--> readElementByIndex, of " + getRole() + " with key " + getKey()); |
|
391 |
read(); |
|
406 | 392 |
|
407 |
@Override |
|
408 |
public Object doWork() { |
|
409 |
final CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this ); |
|
410 |
final CollectionPersister persister = entry.getLoadedPersister(); |
|
411 |
isExtraLazy = persister.isExtraLazy(); |
|
412 |
if ( isExtraLazy ) { |
|
413 |
if ( hasQueuedOperations() ) { |
|
414 |
session.flush(); |
|
393 |
} else { |
|
394 |
class ExtraLazyElementByIndexReader implements LazyInitializationWork { |
|
395 |
private boolean isExtraLazy; |
|
396 |
private Object element; |
|
397 |
|
|
398 |
@Override |
|
399 |
public Object doWork() { |
|
400 |
CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this ); |
|
401 |
CollectionPersister persister = entry.getLoadedPersister(); |
|
402 |
isExtraLazy = persister.isExtraLazy(); |
|
403 |
if ( isExtraLazy ) { |
|
404 |
if ( hasQueuedOperations() ) { |
|
405 |
session.flush(); |
|
406 |
} |
|
407 |
element = persister.getElementByIndex( entry.getLoadedKey(), index, session, owner ); |
|
415 | 408 |
} |
416 |
element = persister.getElementByIndex( entry.getLoadedKey(), index, session, owner );
|
|
417 |
}
|
|
418 |
else {
|
|
419 |
read();
|
|
409 |
else {
|
|
410 |
read();
|
|
411 |
}
|
|
412 |
return null;
|
|
420 | 413 |
} |
421 |
return null; |
|
422 | 414 |
} |
423 |
} |
|
424 | 415 |
|
425 |
final ExtraLazyElementByIndexReader reader = new ExtraLazyElementByIndexReader(); |
|
426 |
//noinspection unchecked |
|
427 |
withTemporarySessionIfNeeded( reader ); |
|
428 |
if ( reader.isExtraLazy ) { |
|
429 |
return reader.element; |
|
416 |
ExtraLazyElementByIndexReader reader = new ExtraLazyElementByIndexReader(); |
|
417 |
//noinspection unchecked |
|
418 |
withTemporarySessionIfNeeded( reader ); |
|
419 |
if ( reader.isExtraLazy ) { |
|
420 |
return reader.element; |
|
421 |
} |
|
430 | 422 |
} |
431 | 423 |
} |
432 | 424 |
return UNKNOWN; |
... | ... | |
437 | 429 |
return cachedSize; |
438 | 430 |
} |
439 | 431 |
|
440 |
protected boolean isConnectedToSession() { |
|
441 |
return session != null |
|
442 |
&& session.isOpen() |
|
443 |
&& session.getPersistenceContext().containsCollection( this ); |
|
444 |
} |
|
445 |
|
|
446 |
protected boolean isInitialized() { |
|
447 |
return initialized; |
|
432 |
private boolean isConnectedToSession() { |
|
433 |
return session != null && |
|
434 |
session.isOpen() && |
|
435 |
session.getPersistenceContext().containsCollection( this ); |
|
448 | 436 |
} |
449 | 437 |
|
450 | 438 |
/** |
... | ... | |
461 | 449 |
*/ |
462 | 450 |
@SuppressWarnings({"JavaDoc"}) |
463 | 451 |
protected boolean isOperationQueueEnabled() { |
464 |
return !initialized |
|
465 |
&& isConnectedToSession()
|
|
466 |
&& isInverseCollection();
|
|
452 |
return !initialized &&
|
|
453 |
isConnectedToSession() &&
|
|
454 |
isInverseCollection(); |
|
467 | 455 |
} |
468 | 456 |
|
469 | 457 |
/** |
... | ... | |
473 | 461 |
*/ |
474 | 462 |
@SuppressWarnings({"JavaDoc"}) |
475 | 463 |
protected boolean isPutQueueEnabled() { |
476 |
return !initialized |
|
477 |
&& isConnectedToSession()
|
|
478 |
&& isInverseOneToManyOrNoOrphanDelete();
|
|
464 |
return !initialized &&
|
|
465 |
isConnectedToSession() &&
|
|
466 |
isInverseOneToManyOrNoOrphanDelete(); |
|
479 | 467 |
} |
480 | 468 |
|
481 | 469 |
/** |
... | ... | |
485 | 473 |
*/ |
486 | 474 |
@SuppressWarnings({"JavaDoc"}) |
487 | 475 |
protected boolean isClearQueueEnabled() { |
488 |
return !initialized |
|
489 |
&& isConnectedToSession()
|
|
490 |
&& isInverseCollectionNoOrphanDelete();
|
|
476 |
return !initialized &&
|
|
477 |
isConnectedToSession() &&
|
|
478 |
isInverseCollectionNoOrphanDelete(); |
|
491 | 479 |
} |
492 | 480 |
|
493 | 481 |
/** |
494 | 482 |
* Is this the "inverse" end of a bidirectional association? |
495 | 483 |
*/ |
496 | 484 |
@SuppressWarnings({"JavaDoc"}) |
497 |
protected boolean isInverseCollection() {
|
|
498 |
final CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
|
|
485 |
private boolean isInverseCollection() {
|
|
486 |
CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this ); |
|
499 | 487 |
return ce != null && ce.getLoadedPersister().isInverse(); |
500 | 488 |
} |
501 | 489 |
|
... | ... | |
504 | 492 |
* no orphan delete enabled? |
505 | 493 |
*/ |
506 | 494 |
@SuppressWarnings({"JavaDoc"}) |
507 |
protected boolean isInverseCollectionNoOrphanDelete() { |
|
508 |
final CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this ); |
|
509 |
return ce != null |
|
510 |
&& |
|
495 |
private boolean isInverseCollectionNoOrphanDelete() { |
|
496 |
CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this ); |
|
497 |
return ce != null && |
|
511 | 498 |
ce.getLoadedPersister().isInverse() && |
512 | 499 |
!ce.getLoadedPersister().hasOrphanDelete(); |
513 | 500 |
} |
... | ... | |
517 | 504 |
* of a collection with no orphan delete? |
518 | 505 |
*/ |
519 | 506 |
@SuppressWarnings({"JavaDoc"}) |
520 |
protected boolean isInverseOneToManyOrNoOrphanDelete() { |
|
521 |
final CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this ); |
|
522 |
return ce != null |
|
523 |
&& ce.getLoadedPersister().isInverse() |
|
524 |
&& ( ce.getLoadedPersister().isOneToMany() || !ce.getLoadedPersister().hasOrphanDelete() ); |
|
507 |
private boolean isInverseOneToManyOrNoOrphanDelete() { |
|
508 |
CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this ); |
|
509 |
return ce != null && ce.getLoadedPersister().isInverse() && ( |
|
510 |
ce.getLoadedPersister().isOneToMany() || |
|
511 |
!ce.getLoadedPersister().hasOrphanDelete() |
|
512 |
); |
|
525 | 513 |
} |
526 | 514 |
|
527 | 515 |
/** |
... | ... | |
533 | 521 |
operationQueue = new ArrayList<DelayedOperation>( 10 ); |
534 | 522 |
} |
535 | 523 |
operationQueue.add( operation ); |
536 |
//needed so that we remove this collection from the second-level cache |
|
537 |
dirty = true; |
|
538 |
} |
|
539 |
|
|
540 |
/** |
|
541 |
* Replace entity instances with copy in {@code copyCache}/. |
|
542 |
* |
|
543 |
* @param copyCache - mapping from entity in the process of being |
|
544 |
* merged to managed copy. |
|
545 |
*/ |
|
546 |
public final void replaceQueuedOperationValues(CollectionPersister persister, Map copyCache) { |
|
547 |
for ( DelayedOperation operation : operationQueue ) { |
|
548 |
if ( ValueDelayedOperation.class.isInstance( operation ) ) { |
|
549 |
( (ValueDelayedOperation) operation ).replace( persister, copyCache ); |
|
550 |
} |
|
551 |
} |
|
524 |
dirty = true; //needed so that we remove this collection from the second-level cache |
|
552 | 525 |
} |
553 | 526 |
|
554 | 527 |
/** |
... | ... | |
561 | 534 |
} |
562 | 535 |
} |
563 | 536 |
|
537 |
/** |
|
538 |
* After flushing, re-init snapshot state. |
|
539 |
*/ |
|
564 | 540 |
@Override |
565 |
public void setSnapshot(Serializable key, String role, Serializable snapshot) {
|
|
541 |
public void setSnapshot(Serializable key, String role, Serializable snapshot) {
|
|
566 | 542 |
this.key = key; |
567 | 543 |
this.role = role; |
568 | 544 |
this.storedSnapshot = snapshot; |
569 | 545 |
} |
570 | 546 |
|
547 |
/** |
|
548 |
* After flushing, clear any "queued" additions, since the |
|
549 |
* database state is now synchronized with the memory state. |
|
550 |
*/ |
|
571 | 551 |
@Override |
572 |
public void postAction() {
|
|
552 |
public void postAction() {
|
|
573 | 553 |
operationQueue = null; |
574 | 554 |
cachedSize = -1; |
575 | 555 |
clearDirty(); |
576 | 556 |
} |
577 | 557 |
|
558 |
/** |
|
559 |
* Not called by Hibernate, but used by non-JDK serialization, |
|
560 |
* eg. SOAP libraries. |
|
561 |
*/ |
|
562 |
public AbstractPersistentCollection() { |
|
563 |
} |
|
564 |
|
|
565 |
protected AbstractPersistentCollection(SessionImplementor session) { |
|
566 |
this.session = session; |
|
567 |
} |
|
568 |
|
|
569 |
/** |
|
570 |
* return the user-visible collection (or array) instance |
|
571 |
*/ |
|
578 | 572 |
@Override |
579 |
public Object getValue() {
|
|
573 |
public Object getValue() {
|
|
580 | 574 |
return this; |
581 | 575 |
} |
582 | 576 |
|
577 |
/** |
|
578 |
* Called just before reading any rows from the JDBC result set |
|
579 |
*/ |
|
583 | 580 |
@Override |
584 |
public void beginRead() {
|
|
581 |
public void beginRead() {
|
|
585 | 582 |
// override on some subclasses |
586 | 583 |
initializing = true; |
587 | 584 |
} |
588 | 585 |
|
586 |
/** |
|
587 |
* Called after reading all rows from the JDBC result set |
|
588 |
*/ |
|
589 | 589 |
@Override |
590 |
public boolean endRead() {
|
|
590 |
public boolean endRead() {
|
|
591 | 591 |
//override on some subclasses |
592 | 592 |
return afterInitialize(); |
593 | 593 |
} |
594 | 594 |
|
595 | 595 |
@Override |
596 |
public boolean afterInitialize() {
|
|
596 |
public boolean afterInitialize() {
|
|
597 | 597 |
setInitialized(); |
598 | 598 |
//do this bit after setting initialized to true or it will recurse |
599 | 599 |
if ( operationQueue != null ) { |
... | ... | |
616 | 616 |
* @throws LazyInitializationException if we cannot initialize |
617 | 617 |
*/ |
618 | 618 |
protected final void initialize(final boolean writing) { |
619 |
if ( initialized ) { |
|
620 |
return; |
|
621 |
} |
|
622 |
|
|
623 |
withTemporarySessionIfNeeded( |
|
624 |
new LazyInitializationWork<Object>() { |
|
625 |
@Override |
|
626 |
public Object doWork() { |
|
627 |
session.initializeCollection( AbstractPersistentCollection.this, writing ); |
|
628 |
return null; |
|
629 |
} |
|
630 |
} |
|
631 |
); |
|
619 |
if ( initialized ) { |
|
620 |
return; |
|
621 |
} |
|
622 |
|
|
623 |
// In remoting we are sure that session is null |
|
624 |
// both when using property paths and switching off conversations |
|
625 |
if(session == null && remoting) { |
|
626 |
remoteInitialize(); |
|
627 |
} else { |
|
628 |
withTemporarySessionIfNeeded( |
|
629 |
new LazyInitializationWork<Object>() { |
|
630 |
@Override |
|
631 |
public Object doWork() { |
|
632 |
session.initializeCollection( AbstractPersistentCollection.this, writing ); |
|
633 |
return null; |
|
634 |
} |
|
635 |
} |
|
636 |
); |
|
637 |
} |
|
632 | 638 |
} |
633 | 639 |
|
634 | 640 |
private void throwLazyInitializationExceptionIfNotConnected() { |
... | ... | |
657 | 663 |
this.directlyAccessible = directlyAccessible; |
658 | 664 |
} |
659 | 665 |
|
666 |
/** |
|
667 |
* Could the application possibly have a direct reference to |
|
668 |
* the underlying collection implementation? |
|
669 |
*/ |
|
660 | 670 |
@Override |
661 |
public boolean isDirectlyAccessible() {
|
|
671 |
public boolean isDirectlyAccessible() {
|
|
662 | 672 |
return directlyAccessible; |
663 | 673 |
} |
664 | 674 |
|
675 |
/** |
|
676 |
* Disassociate this collection from the given session. |
|
677 |
* |
|
678 |
* @return true if this was currently associated with the given session |
|
679 |
*/ |
|
665 | 680 |
@Override |
666 |
public final boolean unsetSession(SessionImplementor currentSession) {
|
|
667 |
prepareForPossibleLoadingOutsideTransaction();
|
|
681 |
public final boolean unsetSession(SessionImplementor currentSession) {
|
|
682 |
prepareForPossibleSpecialSpecjInitialization();
|
|
668 | 683 |
if ( currentSession == this.session ) { |
669 |
if ( !isTempSession ) { |
|
670 |
this.session = null; |
|
671 |
} |
|
684 |
this.session = null; |
|
672 | 685 |
return true; |
673 | 686 |
} |
674 | 687 |
else { |
675 |
if ( this.session != null ) { |
|
676 |
LOG.logCannotUnsetUnexpectedSessionInCollection( generateUnexpectedSessionStateMessage( currentSession ) ); |
|
677 |
} |
|
678 | 688 |
return false; |
679 | 689 |
} |
680 | 690 |
} |
681 | 691 |
|
682 |
protected void prepareForPossibleLoadingOutsideTransaction() {
|
|
692 |
protected void prepareForPossibleSpecialSpecjInitialization() {
|
|
683 | 693 |
if ( session != null ) { |
684 |
allowLoadOutsideTransaction = session.getFactory().getSessionFactoryOptions().isInitializeLazyStateOutsideTransactionsEnabled();
|
|
694 |
specjLazyLoad = session.getFactory().getSettings().isInitializeLazyStateOutsideTransactionsEnabled();
|
|
685 | 695 |
|
686 |
if ( allowLoadOutsideTransaction && sessionFactoryUuid == null ) { |
|
687 |
sessionFactoryUuid = session.getFactory().getUuid(); |
|
696 |
if ( specjLazyLoad && sessionFactoryUuid == null ) { |
|
697 |
try { |
|
698 |
sessionFactoryUuid = (String) session.getFactory().getReference().get( "uuid" ).getContent(); |
|
699 |
} |
|
700 |
catch (NamingException e) { |
|
701 |
//not much we can do if this fails... |
|
702 |
} |
|
688 | 703 |
} |
689 | 704 |
} |
690 | 705 |
} |
691 | 706 |
|
707 |
|
|
708 |
/** |
|
709 |
* Associate the collection with the given session. |
|
710 |
* |
|
711 |
* @return false if the collection was already associated with the session |
|
712 |
* |
|
713 |
* @throws HibernateException if the collection was already associated |
|
714 |
* with another open session |
|
715 |
*/ |
|
692 | 716 |
@Override |
693 |
public final boolean setCurrentSession(SessionImplementor session) throws HibernateException {
|
|
717 |
public final boolean setCurrentSession(SessionImplementor session) throws HibernateException {
|
|
694 | 718 |
if ( session == this.session ) { |
695 | 719 |
return false; |
696 | 720 |
} |
697 | 721 |
else { |
698 |
if ( this.session != null ) {
|
|
699 |
final String msg = generateUnexpectedSessionStateMessage( session );
|
|
700 |
if ( isConnectedToSession() ) {
|
|
722 |
if ( isConnectedToSession() ) {
|
|
723 |
CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
|
|
724 |
if ( ce == null ) {
|
|
701 | 725 |
throw new HibernateException( |
702 |
"Illegal attempt to associate a collection with two open sessions. " + msg
|
|
726 |
"Illegal attempt to associate a collection with two open sessions"
|
|
703 | 727 |
); |
704 | 728 |
} |
705 | 729 |
else { |
706 |
LOG.logUnexpectedSessionInCollectionNotConnected( msg ); |
|
707 |
this.session = session; |
|
708 |
return true; |
|
730 |
throw new HibernateException( |
|
731 |
"Illegal attempt to associate a collection with two open sessions: " + |
|
732 |
MessageHelper.collectionInfoString( |
|
733 |
ce.getLoadedPersister(), this, |
|
734 |
ce.getLoadedKey(), session |
|
735 |
) |
|
736 |
); |
|
709 | 737 |
} |
710 | 738 |
} |
711 | 739 |
else { |
... | ... | |
715 | 743 |
} |
716 | 744 |
} |
717 | 745 |
|
718 |
private String generateUnexpectedSessionStateMessage(SessionImplementor session) { |
|
719 |
// NOTE: If this.session != null, this.session may be operating on this collection |
|
720 |
// (e.g., by changing this.role, this.key, or even this.session) in a different thread. |
|
721 |
|
|
722 |
// Grab the current role and key (it can still get changed by this.session...) |
|
723 |
// If this collection is connected to this.session, then this.role and this.key should |
|
724 |
// be consistent with the CollectionEntry in this.session (as long as this.session doesn't |
|
725 |
// change it). Don't access the CollectionEntry in this.session because that could result |
|
726 |
// in multi-threaded access to this.session. |
|
727 |
final String roleCurrent = role; |
|
728 |
final Serializable keyCurrent = key; |
|
729 |
|
|
730 |
final StringBuilder sb = new StringBuilder( "Collection : " ); |
|
731 |
if ( roleCurrent != null ) { |
|
732 |
sb.append( MessageHelper.collectionInfoString( roleCurrent, keyCurrent ) ); |
|
733 |
} |
|
734 |
else { |
|
735 |
final CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this ); |
|
736 |
if ( ce != null ) { |
|
737 |
sb.append( |
|
738 |
MessageHelper.collectionInfoString( |
|
739 |
ce.getLoadedPersister(), |
|
740 |
this, |
|
741 |
ce.getLoadedKey(), |
|
742 |
session |
|
743 |
) |
|
744 |
); |
|
745 |
} |
|
746 |
else { |
|
747 |
sb.append( "<unknown>" ); |
|
748 |
} |
|
749 |
} |
|
750 |
// only include the collection contents if debug logging |
|
751 |
if ( LOG.isDebugEnabled() ) { |
|
752 |
final String collectionContents = wasInitialized() ? toString() : "<uninitialized>"; |
|
753 |
sb.append( "\nCollection contents: [" ).append( collectionContents ).append( "]" ); |
|
754 |
} |
|
755 |
return sb.toString(); |
|
756 |
} |
|
757 |
|
|
746 |
/** |
|
747 |
* Do we need to completely recreate this collection when it changes? |
|
748 |
*/ |
|
758 | 749 |
@Override |
759 |
public boolean needsRecreate(CollectionPersister persister) { |
|
760 |
// Workaround for situations like HHH-7072. If the collection element is a component that consists entirely |
|
761 |
// of nullable properties, we currently have to forcefully recreate the entire collection. See the use |
|
762 |
// of hasNotNullableColumns in the AbstractCollectionPersister constructor for more info. In order to delete |
|
763 |
// row-by-row, that would require SQL like "WHERE ( COL = ? OR ( COL is null AND ? is null ) )", rather than |
|
764 |
// the current "WHERE COL = ?" (fails for null for most DBs). Note that |
|
765 |
// the param would have to be bound twice. Until we eventually add "parameter bind points" concepts to the |
|
766 |
// AST in ORM 5+, handling this type of condition is either extremely difficult or impossible. Forcing |
|
767 |
// recreation isn't ideal, but not really any other option in ORM 4. |
|
768 |
// Selecting a type used in where part of update statement |
|
769 |
// (must match condidion in org.hibernate.persister.collection.BasicCollectionPersister.doUpdateRows). |
|
770 |
// See HHH-9474 |
|
771 |
Type whereType; |
|
772 |
if ( persister.hasIndex() ) { |
|
773 |
whereType = persister.getIndexType(); |
|
774 |
} |
|
775 |
else { |
|
776 |
whereType = persister.getElementType(); |
|
777 |
} |
|
778 |
if ( whereType instanceof CompositeType ) { |
|
779 |
CompositeType componentIndexType = (CompositeType) whereType; |
|
780 |
return !componentIndexType.hasNotNullProperty(); |
|
781 |
} |
|
750 |
public boolean needsRecreate(CollectionPersister persister) { |
|
782 | 751 |
return false; |
783 | 752 |
} |
784 | 753 |
|
754 |
/** |
|
755 |
* To be called internally by the session, forcing |
|
756 |
* immediate initialization. |
|
757 |
*/ |
|
785 | 758 |
@Override |
786 |
public final void forceInitialization() throws HibernateException {
|
|
759 |
public final void forceInitialization() throws HibernateException {
|
|
787 | 760 |
if ( !initialized ) { |
788 | 761 |
if ( initializing ) { |
789 | 762 |
throw new AssertionFailure( "force initialize loading collection" ); |
790 | 763 |
} |
791 |
initialize( false ); |
|
764 |
if ( session == null ) { |
|
765 |
throw new HibernateException( "collection is not associated with any session" ); |
|
766 |
} |
|
767 |
if ( !session.isConnected() ) { |
|
768 |
throw new HibernateException( "disconnected session" ); |
|
769 |
} |
|
770 |
session.initializeCollection( this, false ); |
|
792 | 771 |
} |
793 | 772 |
} |
794 | 773 |
|
... | ... | |
801 | 780 |
return session.getPersistenceContext().getSnapshot( this ); |
802 | 781 |
} |
803 | 782 |
|
783 |
/** |
|
784 |
* Is this instance initialized? |
|
785 |
*/ |
|
804 | 786 |
@Override |
805 |
public final boolean wasInitialized() {
|
|
787 |
public final boolean wasInitialized() {
|
|
806 | 788 |
return initialized; |
807 | 789 |
} |
808 | 790 |
|
809 | 791 |
@Override |
810 |
public boolean isRowUpdatePossible() {
|
|
792 |
public boolean isRowUpdatePossible() {
|
|
811 | 793 |
return true; |
812 | 794 |
} |
813 | 795 |
|
796 |
/** |
|
797 |
* Does this instance have any "queued" additions? |
|
798 |
*/ |
|
814 | 799 |
@Override |
815 |
public final boolean hasQueuedOperations() {
|
|
800 |
public final boolean hasQueuedOperations() {
|
|
816 | 801 |
return operationQueue != null; |
817 | 802 |
} |
818 | 803 |
|
804 |
/** |
|
805 |
* Iterate the "queued" additions |
|
806 |
*/ |
|
819 | 807 |
@Override |
820 |
public final Iterator queuedAdditionIterator() {
|
|
808 |
public final Iterator queuedAdditionIterator() {
|
|
821 | 809 |
if ( hasQueuedOperations() ) { |
822 | 810 |
return new Iterator() { |
823 |
private int index;
|
|
811 |
int i = 0;
|
|
824 | 812 |
|
825 | 813 |
@Override |
826 |
public Object next() {
|
|
827 |
return operationQueue.get( index++ ).getAddedInstance();
|
|
814 |
public Object next() {
|
|
815 |
return operationQueue.get( i++ ).getAddedInstance(); |
|
828 | 816 |
} |
829 | 817 |
|
830 | 818 |
@Override |
831 |
public boolean hasNext() {
|
|
832 |
return index < operationQueue.size();
|
|
819 |
public boolean hasNext() {
|
|
820 |
return i < operationQueue.size(); |
|
833 | 821 |
} |
834 | 822 |
|
835 | 823 |
@Override |
836 |
public void remove() {
|
|
824 |
public void remove() {
|
|
837 | 825 |
throw new UnsupportedOperationException(); |
838 | 826 |
} |
839 | 827 |
}; |
... | ... | |
843 | 831 |
} |
844 | 832 |
} |
845 | 833 |
|
834 |
/** |
|
835 |
* Iterate the "queued" additions |
|
836 |
*/ |
|
846 | 837 |
@Override |
847 |
@SuppressWarnings({"unchecked"})
|
|
838 |
@SuppressWarnings({"unchecked"})
|
|
848 | 839 |
public final Collection getQueuedOrphans(String entityName) { |
849 | 840 |
if ( hasQueuedOperations() ) { |
850 |
final Collection additions = new ArrayList( operationQueue.size() );
|
|
851 |
final Collection removals = new ArrayList( operationQueue.size() );
|
|
841 |
Collection additions = new ArrayList( operationQueue.size() ); |
|
842 |
Collection removals = new ArrayList( operationQueue.size() ); |
|
852 | 843 |
for ( DelayedOperation operation : operationQueue ) { |
853 | 844 |
additions.add( operation.getAddedInstance() ); |
854 | 845 |
removals.add( operation.getOrphan() ); |
... | ... | |
860 | 851 |
} |
861 | 852 |
} |
862 | 853 |
|
854 |
/** |
|
855 |
* Called before inserting rows, to ensure that any surrogate keys |
|
856 |
* are fully generated |
|
857 |
*/ |
|
863 | 858 |
@Override |
864 |
public void preInsert(CollectionPersister persister) throws HibernateException {
|
|
859 |
public void preInsert(CollectionPersister persister) throws HibernateException {
|
|
865 | 860 |
} |
866 | 861 |
|
862 |
/** |
|
863 |
* Called after inserting a row, to fetch the natively generated id |
|
864 |
*/ |
|
867 | 865 |
@Override |
868 |
public void afterRowInsert(CollectionPersister persister, Object entry, int i) throws HibernateException {
|
|
866 |
public void afterRowInsert(CollectionPersister persister, Object entry, int i) throws HibernateException {
|
|
869 | 867 |
} |
870 | 868 |
|
869 |
/** |
|
870 |
* get all "orphaned" elements |
|
871 |
*/ |
|
871 | 872 |
@Override |
872 |
public abstract Collection getOrphans(Serializable snapshot, String entityName) throws HibernateException;
|
|
873 |
public abstract Collection getOrphans(Serializable snapshot, String entityName) throws HibernateException;
|
|
873 | 874 |
|
874 | 875 |
/** |
875 |
* Get the session currently associated with this collection. |
|
876 |
* |
|
877 |
* @return The session |
|
876 |
* Get the current session |
|
878 | 877 |
*/ |
878 |
@SuppressWarnings({"JavaDoc"}) |
|
879 | 879 |
public final SessionImplementor getSession() { |
880 | 880 |
return session; |
881 | 881 |
} |
... | ... | |
888 | 888 |
} |
889 | 889 |
|
890 | 890 |
@Override |
891 |
public boolean hasNext() {
|
|
891 |
public boolean hasNext() {
|
|
892 | 892 |
return itr.hasNext(); |
893 | 893 |
} |
894 | 894 |
|
895 | 895 |
@Override |
896 |
public Object next() {
|
|
896 |
public Object next() {
|
|
897 | 897 |
return itr.next(); |
898 | 898 |
} |
899 | 899 |
|
900 | 900 |
@Override |
901 |
public void remove() {
|
|
901 |
public void remove() {
|
|
902 | 902 |
write(); |
903 | 903 |
itr.remove(); |
904 | 904 |
} |
905 |
|
|
905 | 906 |
} |
906 | 907 |
|
907 | 908 |
protected final class ListIteratorProxy implements ListIterator { |
... | ... | |
912 | 913 |
} |
913 | 914 |
|
914 | 915 |
@Override |
915 |
@SuppressWarnings({"unchecked"})
|
|
916 |
@SuppressWarnings({"unchecked"})
|
|
916 | 917 |
public void add(Object o) { |
917 | 918 |
write(); |
918 | 919 |
itr.add( o ); |
919 | 920 |
} |
920 | 921 |
|
921 | 922 |
@Override |
922 |
public boolean hasNext() {
|
|
923 |
public boolean hasNext() {
|
|
923 | 924 |
return itr.hasNext(); |
924 | 925 |
} |
925 | 926 |
|
926 | 927 |
@Override |
927 |
public boolean hasPrevious() {
|
|
928 |
public boolean hasPrevious() {
|
|
928 | 929 |
return itr.hasPrevious(); |
929 | 930 |
} |
930 | 931 |
|
931 | 932 |
@Override |
932 |
public Object next() {
|
|
933 |
public Object next() {
|
|
933 | 934 |
return itr.next(); |
934 | 935 |
} |
935 | 936 |
|
936 | 937 |
@Override |
937 |
public int nextIndex() {
|
|
938 |
public int nextIndex() {
|
|
938 | 939 |
return itr.nextIndex(); |
939 | 940 |
} |
940 | 941 |
|
941 | 942 |
@Override |
942 |
public Object previous() {
|
|
943 |
public Object previous() {
|
|
943 | 944 |
return itr.previous(); |
944 | 945 |
} |
945 | 946 |
|
946 | 947 |
@Override |
947 |
public int previousIndex() {
|
|
948 |
public int previousIndex() {
|
|
948 | 949 |
return itr.previousIndex(); |
949 | 950 |
} |
950 | 951 |
|
951 | 952 |
@Override |
952 |
public void remove() {
|
|
953 |
public void remove() {
|
|
953 | 954 |
write(); |
954 | 955 |
itr.remove(); |
955 | 956 |
} |
956 | 957 |
|
957 | 958 |
@Override |
958 |
@SuppressWarnings({"unchecked"})
|
|
959 |
@SuppressWarnings({"unchecked"})
|
|
959 | 960 |
public void set(Object o) { |
960 | 961 |
write(); |
961 | 962 |
itr.set( o ); |
962 | 963 |
} |
964 |
|
|
963 | 965 |
} |
964 | 966 |
|
965 | 967 |
protected class SetProxy implements java.util.Set { |
... | ... | |
970 | 972 |
} |
971 | 973 |
|
972 | 974 |
@Override |
973 |
@SuppressWarnings({"unchecked"})
|
|
975 |
@SuppressWarnings({"unchecked"})
|
|
974 | 976 |
public boolean add(Object o) { |
975 | 977 |
write(); |
976 | 978 |
return set.add( o ); |
977 | 979 |
} |
978 | 980 |
|
979 | 981 |
@Override |
980 |
@SuppressWarnings({"unchecked"})
|
|
982 |
@SuppressWarnings({"unchecked"})
|
|
981 | 983 |
public boolean addAll(Collection c) { |
982 | 984 |
write(); |
983 | 985 |
return set.addAll( c ); |
984 | 986 |
} |
985 | 987 |
|
986 | 988 |
@Override |
987 |
public void clear() {
|
|
989 |
public void clear() {
|
|
988 | 990 |
write(); |
989 | 991 |
set.clear(); |
990 | 992 |
} |
991 | 993 |
|
992 | 994 |
@Override |
993 |
public boolean contains(Object o) {
|
|
995 |
public boolean contains(Object o) {
|
|
994 | 996 |
return set.contains( o ); |
995 | 997 |
} |
996 | 998 |
|
997 | 999 |
@Override |
998 |
@SuppressWarnings("unchecked") |
|
999 |
public boolean containsAll(Collection c) { |
|
1000 |
public boolean containsAll(Collection c) { |
|
1000 | 1001 |
return set.containsAll( c ); |
1001 | 1002 |
} |
1002 | 1003 |
|
1003 | 1004 |
@Override |
1004 |
public boolean isEmpty() {
|
|
1005 |
public boolean isEmpty() {
|
|
1005 | 1006 |
return set.isEmpty(); |
1006 | 1007 |
} |
1007 | 1008 |
|
1008 | 1009 |
@Override |
1009 |
public Iterator iterator() {
|
|
1010 |
public Iterator iterator() {
|
|
1010 | 1011 |
return new IteratorProxy( set.iterator() ); |
1011 | 1012 |
} |
1012 | 1013 |
|
1013 | 1014 |
@Override |
1014 |
public boolean remove(Object o) {
|
|
1015 |
public boolean remove(Object o) {
|
|
1015 | 1016 |
write(); |
1016 | 1017 |
return set.remove( o ); |
1017 | 1018 |
} |
1018 | 1019 |
|
1019 | 1020 |
@Override |
1020 |
@SuppressWarnings("unchecked") |
|
1021 |
public boolean removeAll(Collection c) { |
|
1021 |
public boolean removeAll(Collection c) { |
|
1022 | 1022 |
write(); |
1023 | 1023 |
return set.removeAll( c ); |
1024 | 1024 |
} |
1025 | 1025 |
|
1026 | 1026 |
@Override |
1027 |
@SuppressWarnings("unchecked") |
|
1028 |
public boolean retainAll(Collection c) { |
|
1027 |
public boolean retainAll(Collection c) { |
|
1029 | 1028 |
write(); |
1030 | 1029 |
return set.retainAll( c ); |
1031 | 1030 |
} |
1032 | 1031 |
|
1033 | 1032 |
@Override |
1034 |
public int size() {
|
|
1033 |
public int size() {
|
|
1035 | 1034 |
return set.size(); |
1036 | 1035 |
} |
1037 | 1036 |
|
1038 | 1037 |
@Override |
1039 |
public Object[] toArray() {
|
|
1038 |
public Object[] toArray() {
|
|
1040 | 1039 |
return set.toArray(); |
1041 | 1040 |
} |
1042 | 1041 |
|
1043 | 1042 |
@Override |
1044 |
@SuppressWarnings({"unchecked"})
|
|
1043 |
@SuppressWarnings({"unchecked"})
|
|
1045 | 1044 |
public Object[] toArray(Object[] array) { |
1046 | 1045 |
return set.toArray( array ); |
1047 | 1046 |
} |
1047 |
|
|
1048 | 1048 |
} |
1049 | 1049 |
|
1050 | 1050 |
protected final class ListProxy implements java.util.List { |
... | ... | |
1094 | 1094 |
} |
1095 | 1095 |
|
1096 | 1096 |
@Override |
1097 |
@SuppressWarnings("unchecked") |
|
1098 | 1097 |
public boolean containsAll(Collection c) { |
1099 | 1098 |
return list.containsAll( c ); |
1100 | 1099 |
} |
... | ... | |
1147 | 1146 |
} |
1148 | 1147 |
|
1149 | 1148 |
@Override |
1150 |
@SuppressWarnings("unchecked") |
|
1151 | 1149 |
public boolean removeAll(Collection c) { |
1152 | 1150 |
write(); |
1153 | 1151 |
return list.removeAll( c ); |
1154 | 1152 |
} |
1155 | 1153 |
|
1156 | 1154 |
@Override |
1157 |
@SuppressWarnings("unchecked") |
|
1158 | 1155 |
public boolean retainAll(Collection c) { |
1159 | 1156 |
write(); |
1160 | 1157 |
return list.retainAll( c ); |
... | ... | |
1201 | 1198 |
public Object getOrphan(); |
1202 | 1199 |
} |
1203 | 1200 |
|
1204 |
protected interface ValueDelayedOperation extends DelayedOperation { |
|
1205 |
void replace(CollectionPersister collectionPersister, Map copyCache); |
|
1206 |
} |
|
1207 |
|
|
1208 |
protected abstract class AbstractValueDelayedOperation implements ValueDelayedOperation { |
|
1209 |
private Object addedValue; |
|
1210 |
private Object orphan; |
|
1211 |
|
|
1212 |
protected AbstractValueDelayedOperation(Object addedValue, Object orphan) { |
|
1213 |
this.addedValue = addedValue; |
|
1214 |
this.orphan = orphan; |
|
1215 |
} |
|
1216 |
|
|
1217 |
public void replace(CollectionPersister persister, Map copyCache) { |
|
1218 |
if ( addedValue != null ) { |
|
1219 |
addedValue = getReplacement( persister.getElementType(), addedValue, copyCache ); |
|
1220 |
} |
|
1221 |
} |
|
1222 |
|
|
1223 |
protected final Object getReplacement(Type type, Object current, Map copyCache) { |
|
1224 |
return type.replace( current, null, session, owner, copyCache ); |
|
1225 |
} |
|
1226 |
|
|
1227 |
@Override |
|
1228 |
public final Object getAddedInstance() { |
|
1229 |
return addedValue; |
|
1230 |
} |
|
1231 |
|
|
1232 |
@Override |
|
1233 |
public final Object getOrphan() { |
|
1234 |
return orphan; |
|
1235 |
} |
|
1236 |
} |
|
1237 |
|
|
1238 | 1201 |
/** |
1239 | 1202 |
* Given a collection of entity instances that used to |
1240 | 1203 |
* belong to the collection, and a collection of instances |
... | ... | |
1249 | 1212 |
|
1250 | 1213 |
// short-circuit(s) |
1251 | 1214 |
if ( currentElements.size() == 0 ) { |
1252 |
// no new elements, the old list contains only Orphans |
|
1253 |
return oldElements; |
|
1215 |
return oldElements; // no new elements, the old list contains only Orphans |
|
1254 | 1216 |
} |
1255 | 1217 |
if ( oldElements.size() == 0 ) { |
1256 |
// no old elements, so no Orphans neither |
|
1257 |
return oldElements; |
|
1218 |
return oldElements; // no old elements, so no Orphans neither |
|
1258 | 1219 |
} |
1259 | 1220 |
|
1260 | 1221 |
final EntityPersister entityPersister = session.getFactory().getEntityPersister( entityName ); |
1261 | 1222 |
final Type idType = entityPersister.getIdentifierType(); |
1262 |
final boolean useIdDirect = mayUseIdDirect( idType ); |
|
1263 | 1223 |
|
1264 | 1224 |
// create the collection holding the Orphans |
1265 |
final Collection res = new ArrayList();
|
|
1225 |
Collection res = new ArrayList(); |
|
1266 | 1226 |
|
1267 | 1227 |
// collect EntityIdentifier(s) of the *current* elements - add them into a HashSet for fast access |
1268 |
final java.util.Set currentIds = new HashSet();
|
|
1269 |
final java.util.Set currentSaving = new IdentitySet();
|
|
1228 |
java.util.Set currentIds = new HashSet(); |
|
1229 |
java.util.Set currentSaving = new IdentitySet(); |
|
1270 | 1230 |
for ( Object current : currentElements ) { |
1271 | 1231 |
if ( current != null && ForeignKeys.isNotTransient( entityName, current, null, session ) ) { |
1272 |
final EntityEntry ee = session.getPersistenceContext().getEntry( current );
|
|
1232 |
EntityEntry ee = session.getPersistenceContext().getEntry( current ); |
|
1273 | 1233 |
if ( ee != null && ee.getStatus() == Status.SAVING ) { |
1274 | 1234 |
currentSaving.add( current ); |
1275 | 1235 |
} |
1276 | 1236 |
else { |
1277 |
final Serializable currentId = ForeignKeys.getEntityIdentifierIfNotUnsaved(
|
|
1237 |
Serializable currentId = ForeignKeys.getEntityIdentifierIfNotUnsaved( |
|
1278 | 1238 |
entityName, |
1279 | 1239 |
current, |
1280 | 1240 |
session |
1281 | 1241 |
); |
1282 |
currentIds.add( useIdDirect ? currentId : new TypedValue( idType, currentId ) );
|
|
1242 |
currentIds.add( new TypedValue( idType, currentId, entityPersister.getEntityMode() ) );
|
|
1283 | 1243 |
} |
1284 | 1244 |
} |
1285 | 1245 |
} |
... | ... | |
1287 | 1247 |
// iterate over the *old* list |
1288 | 1248 |
for ( Object old : oldElements ) { |
1289 | 1249 |
if ( !currentSaving.contains( old ) ) { |
1290 |
final Serializable oldId = ForeignKeys.getEntityIdentifierIfNotUnsaved( entityName, old, session );
|
|
1291 |
if ( !currentIds.contains( useIdDirect ? oldId : new TypedValue( idType, oldId ) ) ) {
|
|
1250 |
Serializable oldId = ForeignKeys.getEntityIdentifierIfNotUnsaved( entityName, old, session ); |
|
1251 |
if ( !currentIds.contains( new TypedValue( idType, oldId, entityPersister.getEntityMode() ) ) ) {
|
|
1292 | 1252 |
res.add( old ); |
1293 | 1253 |
} |
1294 | 1254 |
} |
... | ... | |
1297 | 1257 |
return res; |
1298 | 1258 |
} |
1299 | 1259 |
|
1300 |
private static boolean mayUseIdDirect(Type idType) { |
|
1301 |
return idType == StringType.INSTANCE |
|
1302 |
|| idType == IntegerType.INSTANCE |
|
1303 |
|| idType == LongType.INSTANCE |
|
1304 |
|| idType == UUIDBinaryType.INSTANCE |
|
1305 |
|| idType == UUIDCharType.INSTANCE |
|
1306 |
|| idType == PostgresUUIDType.INSTANCE; |
|
1307 |
} |
|
1308 |
|
|
1309 |
/** |
|
1310 |
* Removes entity entries that have an equal identifier with the incoming entity instance |
|
1311 |
* |
|
1312 |
* @param list The list containing the entity instances |
|
1313 |
* @param entityInstance The entity instance to match elements. |
|
1314 |
* @param entityName The entity name |
|
1315 |
* @param session The session |
|
1316 |
*/ |
|
1317 | 1260 |
public static void identityRemove( |
1318 | 1261 |
Collection list, |
1319 |
Object entityInstance,
|
|
1262 |
Object object,
|
|
1320 | 1263 |
String entityName, |
1321 |
SessionImplementor session) { |
|
1264 |
SessionImplementor session) throws HibernateException {
|
|
1322 | 1265 |
|
1323 |
if ( entityInstance != null && ForeignKeys.isNotTransient( entityName, entityInstance, null, session ) ) {
|
|
1266 |
if ( object != null && ForeignKeys.isNotTransient( entityName, object, null, session ) ) {
|
|
1324 | 1267 |
final EntityPersister entityPersister = session.getFactory().getEntityPersister( entityName ); |
1325 |
final Type idType = entityPersister.getIdentifierType();
|
|
1268 |
Type idType = entityPersister.getIdentifierType(); |
|
1326 | 1269 |
|
1327 |
final Serializable idOfCurrent = ForeignKeys.getEntityIdentifierIfNotUnsaved( entityName, entityInstance, session );
|
|
1328 |
final Iterator itr = list.iterator();
|
|
1270 |
Serializable idOfCurrent = ForeignKeys.getEntityIdentifierIfNotUnsaved( entityName, object, session );
|
|
1271 |
Iterator itr = list.iterator(); |
|
1329 | 1272 |
while ( itr.hasNext() ) { |
1330 |
final Serializable idOfOld = ForeignKeys.getEntityIdentifierIfNotUnsaved( entityName, itr.next(), session );
|
|
1273 |
Serializable idOfOld = ForeignKeys.getEntityIdentifierIfNotUnsaved( entityName, itr.next(), session ); |
|
1331 | 1274 |
if ( idType.isEqual( idOfCurrent, idOfOld, session.getFactory() ) ) { |
1332 | 1275 |
itr.remove(); |
1333 | 1276 |
break; |
... | ... | |
1338 | 1281 |
} |
1339 | 1282 |
|
1340 | 1283 |
@Override |
1341 |
public Object getIdentifier(Object entry, int i) {
|
|
1284 |
public Object getIdentifier(Object entry, int i) {
|
|
1342 | 1285 |
throw new UnsupportedOperationException(); |
1343 | 1286 |
} |
1344 | 1287 |
|
1345 | 1288 |
@Override |
1346 |
public Object getOwner() {
|
|
1289 |
public Object getOwner() {
|
|
1347 | 1290 |
return owner; |
1348 | 1291 |
} |
1349 | 1292 |
|
1350 | 1293 |
@Override |
1351 |
public void setOwner(Object owner) {
|
|
1294 |
public void setOwner(Object owner) {
|
|
1352 | 1295 |
this.owner = owner; |
1353 | 1296 |
} |
1354 | 1297 |
|
... | ... | |
1374 | 1317 |
configuration = conf; |
1375 | 1318 |
} |
1376 | 1319 |
|
1377 |
|
|
1378 | 1320 |
private void remoteInitialize() { |
1379 | 1321 |
|
1380 | 1322 |
if (getOwner() != null && !initialized) { |
... | ... | |
1382 | 1324 |
try { |
1383 | 1325 |
String role = getRole(); |
1384 | 1326 |
String fieldName = role.substring(role.lastIndexOf(".") + 1); |
1385 |
LOG.info("--> Remote Lazy Initializing Collection " + getRole() + " , owner : " + getOwner().getClass() + "/" + getKey() + " , field : " + fieldName);
|
|
1327 |
log.info("--> Remote Lazy Initializing Collection " + getRole() + " , owner : " + getOwner().getClass() + "/" + getKey() + " , field : " + fieldName);
|
|
1386 | 1328 |
Object owner = getOwner(); |
1387 | 1329 |
CdmBase cdmBase; |
1388 | 1330 |
if(owner instanceof CdmBase) { |
Also available in: Unified diff
ref #9204 revert changes to AbstractPersistentCollection, only keep new serialVersionUID (for doing it step by step)