1
1
/*
2
- * Copyright 2002-2014 the original author or authors.
2
+ * Copyright 2002-2015 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
30
30
import java .util .Map ;
31
31
import java .util .Properties ;
32
32
import java .util .Set ;
33
+ import java .util .concurrent .Callable ;
34
+ import java .util .concurrent .ExecutionException ;
35
+ import java .util .concurrent .Future ;
33
36
import javax .persistence .EntityManager ;
34
37
import javax .persistence .EntityManagerFactory ;
35
38
import javax .persistence .PersistenceException ;
48
51
import org .springframework .beans .factory .DisposableBean ;
49
52
import org .springframework .beans .factory .FactoryBean ;
50
53
import org .springframework .beans .factory .InitializingBean ;
54
+ import org .springframework .core .task .AsyncTaskExecutor ;
51
55
import org .springframework .dao .DataAccessException ;
52
56
import org .springframework .dao .support .PersistenceExceptionTranslator ;
53
57
import org .springframework .util .Assert ;
@@ -102,15 +106,21 @@ public abstract class AbstractEntityManagerFactoryBean implements
102
106
103
107
private JpaVendorAdapter jpaVendorAdapter ;
104
108
109
+ private AsyncTaskExecutor bootstrapExecutor ;
110
+
105
111
private ClassLoader beanClassLoader = getClass ().getClassLoader ();
106
112
107
113
private BeanFactory beanFactory ;
108
114
109
115
private String beanName ;
110
116
111
117
/** Raw EntityManagerFactory as returned by the PersistenceProvider */
112
- public EntityManagerFactory nativeEntityManagerFactory ;
118
+ private EntityManagerFactory nativeEntityManagerFactory ;
119
+
120
+ /** Future for lazily initializing raw target EntityManagerFactory */
121
+ private Future <EntityManagerFactory > nativeEntityManagerFactoryFuture ;
113
122
123
+ /** Exposed client-level EntityManagerFactory proxy */
114
124
private EntityManagerFactory entityManagerFactory ;
115
125
116
126
@@ -263,6 +273,30 @@ public JpaVendorAdapter getJpaVendorAdapter() {
263
273
return this .jpaVendorAdapter ;
264
274
}
265
275
276
+ /**
277
+ * Specify an asynchronous executor for background bootstrapping,
278
+ * e.g. a {@link org.springframework.core.task.SimpleAsyncTaskExecutor}.
279
+ * <p>{@code EntityManagerFactory} initialization will then switch into background
280
+ * bootstrap mode, with a {@code EntityManagerFactory} proxy immediately returned for
281
+ * injection purposes instead of waiting for the JPA provider's bootstrapping to complete.
282
+ * However, note that the first actual call to a {@code EntityManagerFactory} method will
283
+ * then block until the JPA provider's bootstrapping completed, if not ready by then.
284
+ * For maximum benefit, make sure to avoid early {@code EntityManagerFactory} calls
285
+ * in init methods of related beans, even for metadata introspection purposes.
286
+ * @since 4.3
287
+ */
288
+ public void setBootstrapExecutor (AsyncTaskExecutor bootstrapExecutor ) {
289
+ this .bootstrapExecutor = bootstrapExecutor ;
290
+ }
291
+
292
+ /**
293
+ * Return the asynchronous executor for background bootstrapping, if any.
294
+ * @since 4.3
295
+ */
296
+ public AsyncTaskExecutor getBootstrapExecutor () {
297
+ return this .bootstrapExecutor ;
298
+ }
299
+
266
300
@ Override
267
301
public void setBeanClassLoader (ClassLoader classLoader ) {
268
302
this .beanClassLoader = classLoader ;
@@ -315,20 +349,40 @@ public final void afterPropertiesSet() throws PersistenceException {
315
349
}
316
350
}
317
351
318
- this .nativeEntityManagerFactory = createNativeEntityManagerFactory ();
319
- if (this .nativeEntityManagerFactory == null ) {
320
- throw new IllegalStateException (
321
- "JPA PersistenceProvider returned null EntityManagerFactory - check your JPA provider setup!" );
352
+ if (this .bootstrapExecutor != null ) {
353
+ this .nativeEntityManagerFactoryFuture = this .bootstrapExecutor .submit (new Callable <EntityManagerFactory >() {
354
+ @ Override
355
+ public EntityManagerFactory call () {
356
+ return buildNativeEntityManagerFactory ();
357
+ }
358
+ });
322
359
}
323
- if ( this . jpaVendorAdapter != null ) {
324
- this .jpaVendorAdapter . postProcessEntityManagerFactory ( this . nativeEntityManagerFactory );
360
+ else {
361
+ this .nativeEntityManagerFactory = buildNativeEntityManagerFactory ( );
325
362
}
326
363
327
364
// Wrap the EntityManagerFactory in a factory implementing all its interfaces.
328
365
// This allows interception of createEntityManager methods to return an
329
366
// application-managed EntityManager proxy that automatically joins
330
367
// existing transactions.
331
368
this .entityManagerFactory = createEntityManagerFactoryProxy (this .nativeEntityManagerFactory );
369
+ System .out .println ("Returning: " + System .currentTimeMillis ());
370
+ }
371
+
372
+ private EntityManagerFactory buildNativeEntityManagerFactory () {
373
+ EntityManagerFactory emf = createNativeEntityManagerFactory ();
374
+ if (emf == null ) {
375
+ throw new IllegalStateException (
376
+ "JPA PersistenceProvider returned null EntityManagerFactory - check your JPA provider setup!" );
377
+ }
378
+ if (this .jpaVendorAdapter != null ) {
379
+ this .jpaVendorAdapter .postProcessEntityManagerFactory (emf );
380
+ }
381
+ if (logger .isInfoEnabled ()) {
382
+ logger .info ("Initialized JPA EntityManagerFactory for persistence unit '" + getPersistenceUnitName () + "'" );
383
+ }
384
+ System .out .println ("Done: " + System .currentTimeMillis ());
385
+ return emf ;
332
386
}
333
387
334
388
/**
@@ -343,9 +397,12 @@ protected EntityManagerFactory createEntityManagerFactoryProxy(EntityManagerFact
343
397
if (this .entityManagerFactoryInterface != null ) {
344
398
ifcs .add (this .entityManagerFactoryInterface );
345
399
}
346
- else {
400
+ else if ( emf != null ) {
347
401
ifcs .addAll (ClassUtils .getAllInterfacesForClassAsSet (emf .getClass (), this .beanClassLoader ));
348
402
}
403
+ else {
404
+ ifcs .add (EntityManagerFactory .class );
405
+ }
349
406
ifcs .add (EntityManagerFactoryInfo .class );
350
407
try {
351
408
return (EntityManagerFactory ) Proxy .newProxyInstance (
@@ -379,13 +436,13 @@ else if (method.getName().equals("createEntityManager") && args != null && args.
379
436
// JPA 2.1's createEntityManager(SynchronizationType, Map)
380
437
// Redirect to plain createEntityManager and add synchronization semantics through Spring proxy
381
438
EntityManager rawEntityManager = (args .length > 1 ?
382
- this . nativeEntityManagerFactory .createEntityManager ((Map <?, ?>) args [1 ]) :
383
- this . nativeEntityManagerFactory .createEntityManager ());
439
+ getNativeEntityManagerFactory () .createEntityManager ((Map <?, ?>) args [1 ]) :
440
+ getNativeEntityManagerFactory () .createEntityManager ());
384
441
return ExtendedEntityManagerCreator .createApplicationManagedEntityManager (rawEntityManager , this , true );
385
442
}
386
443
387
444
// Standard delegation to the native factory, just post-processing EntityManager return values
388
- Object retVal = method .invoke (this . nativeEntityManagerFactory , args );
445
+ Object retVal = method .invoke (getNativeEntityManagerFactory () , args );
389
446
if (retVal instanceof EntityManager ) {
390
447
// Any other createEntityManager variant - expecting non-synchronized semantics
391
448
EntityManager rawEntityManager = (EntityManager ) retVal ;
@@ -420,7 +477,21 @@ public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
420
477
421
478
@ Override
422
479
public EntityManagerFactory getNativeEntityManagerFactory () {
423
- return this .nativeEntityManagerFactory ;
480
+ if (this .nativeEntityManagerFactory != null ) {
481
+ return this .nativeEntityManagerFactory ;
482
+ }
483
+ else {
484
+ System .out .println ("Requested: " + System .currentTimeMillis ());
485
+ try {
486
+ return this .nativeEntityManagerFactoryFuture .get ();
487
+ }
488
+ catch (InterruptedException ex ) {
489
+ throw new IllegalStateException ("Interrupted during initialization of native EntityManagerFactory" , ex );
490
+ }
491
+ catch (ExecutionException ex ) {
492
+ throw new IllegalStateException ("Failed to asynchronously initialize native EntityManagerFactory" , ex );
493
+ }
494
+ }
424
495
}
425
496
426
497
@ Override
0 commit comments