001/* 002 * Copyright 2002-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.orm.jpa; 018 019import java.io.IOException; 020import java.io.NotSerializableException; 021import java.io.ObjectInputStream; 022import java.io.ObjectStreamException; 023import java.io.Serializable; 024import java.lang.reflect.InvocationHandler; 025import java.lang.reflect.InvocationTargetException; 026import java.lang.reflect.Method; 027import java.lang.reflect.Proxy; 028import java.util.HashMap; 029import java.util.LinkedHashSet; 030import java.util.Map; 031import java.util.Properties; 032import java.util.Set; 033import java.util.concurrent.Callable; 034import java.util.concurrent.ExecutionException; 035import java.util.concurrent.Future; 036import javax.persistence.EntityManager; 037import javax.persistence.EntityManagerFactory; 038import javax.persistence.PersistenceException; 039import javax.persistence.Query; 040import javax.persistence.spi.PersistenceProvider; 041import javax.persistence.spi.PersistenceUnitInfo; 042import javax.sql.DataSource; 043 044import org.apache.commons.logging.Log; 045import org.apache.commons.logging.LogFactory; 046 047import org.springframework.beans.BeanUtils; 048import org.springframework.beans.factory.BeanClassLoaderAware; 049import org.springframework.beans.factory.BeanFactory; 050import org.springframework.beans.factory.BeanFactoryAware; 051import org.springframework.beans.factory.BeanNameAware; 052import org.springframework.beans.factory.DisposableBean; 053import org.springframework.beans.factory.FactoryBean; 054import org.springframework.beans.factory.InitializingBean; 055import org.springframework.core.task.AsyncTaskExecutor; 056import org.springframework.dao.DataAccessException; 057import org.springframework.dao.support.PersistenceExceptionTranslator; 058import org.springframework.util.ClassUtils; 059import org.springframework.util.CollectionUtils; 060 061/** 062 * Abstract {@link org.springframework.beans.factory.FactoryBean} that creates 063 * a local JPA {@link javax.persistence.EntityManagerFactory} instance within 064 * a Spring application context. 065 * 066 * <p>Encapsulates the common functionality between the different JPA bootstrap 067 * contracts (standalone as well as container). 068 * 069 * <p>Implements support for standard JPA configuration conventions as well as 070 * Spring's customizable {@link JpaVendorAdapter} mechanism, and controls the 071 * EntityManagerFactory's lifecycle. 072 * 073 * <p>This class also implements the 074 * {@link org.springframework.dao.support.PersistenceExceptionTranslator} 075 * interface, as autodetected by Spring's 076 * {@link org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor}, 077 * for AOP-based translation of native exceptions to Spring DataAccessExceptions. 078 * Hence, the presence of e.g. LocalEntityManagerFactoryBean automatically enables 079 * a PersistenceExceptionTranslationPostProcessor to translate JPA exceptions. 080 * 081 * @author Juergen Hoeller 082 * @author Rod Johnson 083 * @since 2.0 084 * @see LocalEntityManagerFactoryBean 085 * @see LocalContainerEntityManagerFactoryBean 086 */ 087@SuppressWarnings("serial") 088public abstract class AbstractEntityManagerFactoryBean implements 089 FactoryBean<EntityManagerFactory>, BeanClassLoaderAware, BeanFactoryAware, BeanNameAware, 090 InitializingBean, DisposableBean, EntityManagerFactoryInfo, PersistenceExceptionTranslator, Serializable { 091 092 /** Logger available to subclasses */ 093 protected final Log logger = LogFactory.getLog(getClass()); 094 095 private PersistenceProvider persistenceProvider; 096 097 private String persistenceUnitName; 098 099 private final Map<String, Object> jpaPropertyMap = new HashMap<String, Object>(); 100 101 private Class<? extends EntityManagerFactory> entityManagerFactoryInterface; 102 103 private Class<? extends EntityManager> entityManagerInterface; 104 105 private JpaDialect jpaDialect; 106 107 private JpaVendorAdapter jpaVendorAdapter; 108 109 private AsyncTaskExecutor bootstrapExecutor; 110 111 private ClassLoader beanClassLoader = getClass().getClassLoader(); 112 113 private BeanFactory beanFactory; 114 115 private String beanName; 116 117 /** Raw EntityManagerFactory as returned by the PersistenceProvider */ 118 private EntityManagerFactory nativeEntityManagerFactory; 119 120 /** Future for lazily initializing raw target EntityManagerFactory */ 121 private Future<EntityManagerFactory> nativeEntityManagerFactoryFuture; 122 123 /** Exposed client-level EntityManagerFactory proxy */ 124 private EntityManagerFactory entityManagerFactory; 125 126 127 /** 128 * Set the PersistenceProvider implementation class to use for creating the 129 * EntityManagerFactory. If not specified, the persistence provider will be 130 * taken from the JpaVendorAdapter (if any) or retrieved through scanning 131 * (as far as possible). 132 * @see JpaVendorAdapter#getPersistenceProvider() 133 * @see javax.persistence.spi.PersistenceProvider 134 * @see javax.persistence.Persistence 135 */ 136 public void setPersistenceProviderClass(Class<? extends PersistenceProvider> persistenceProviderClass) { 137 this.persistenceProvider = BeanUtils.instantiateClass(persistenceProviderClass); 138 } 139 140 /** 141 * Set the PersistenceProvider instance to use for creating the 142 * EntityManagerFactory. If not specified, the persistence provider 143 * will be taken from the JpaVendorAdapter (if any) or determined 144 * by the persistence unit deployment descriptor (as far as possible). 145 * @see JpaVendorAdapter#getPersistenceProvider() 146 * @see javax.persistence.spi.PersistenceProvider 147 * @see javax.persistence.Persistence 148 */ 149 public void setPersistenceProvider(PersistenceProvider persistenceProvider) { 150 this.persistenceProvider = persistenceProvider; 151 } 152 153 @Override 154 public PersistenceProvider getPersistenceProvider() { 155 return this.persistenceProvider; 156 } 157 158 /** 159 * Specify the name of the EntityManagerFactory configuration. 160 * <p>Default is none, indicating the default EntityManagerFactory 161 * configuration. The persistence provider will throw an exception if 162 * ambiguous EntityManager configurations are found. 163 * @see javax.persistence.Persistence#createEntityManagerFactory(String) 164 */ 165 public void setPersistenceUnitName(String persistenceUnitName) { 166 this.persistenceUnitName = persistenceUnitName; 167 } 168 169 @Override 170 public String getPersistenceUnitName() { 171 return this.persistenceUnitName; 172 } 173 174 /** 175 * Specify JPA properties, to be passed into 176 * {@code Persistence.createEntityManagerFactory} (if any). 177 * <p>Can be populated with a String "value" (parsed via PropertiesEditor) or a 178 * "props" element in XML bean definitions. 179 * @see javax.persistence.Persistence#createEntityManagerFactory(String, java.util.Map) 180 * @see javax.persistence.spi.PersistenceProvider#createContainerEntityManagerFactory(javax.persistence.spi.PersistenceUnitInfo, java.util.Map) 181 */ 182 public void setJpaProperties(Properties jpaProperties) { 183 CollectionUtils.mergePropertiesIntoMap(jpaProperties, this.jpaPropertyMap); 184 } 185 186 /** 187 * Specify JPA properties as a Map, to be passed into 188 * {@code Persistence.createEntityManagerFactory} (if any). 189 * <p>Can be populated with a "map" or "props" element in XML bean definitions. 190 * @see javax.persistence.Persistence#createEntityManagerFactory(String, java.util.Map) 191 * @see javax.persistence.spi.PersistenceProvider#createContainerEntityManagerFactory(javax.persistence.spi.PersistenceUnitInfo, java.util.Map) 192 */ 193 public void setJpaPropertyMap(Map<String, ?> jpaProperties) { 194 if (jpaProperties != null) { 195 this.jpaPropertyMap.putAll(jpaProperties); 196 } 197 } 198 199 /** 200 * Allow Map access to the JPA properties to be passed to the persistence 201 * provider, with the option to add or override specific entries. 202 * <p>Useful for specifying entries directly, for example via 203 * "jpaPropertyMap[myKey]". 204 */ 205 public Map<String, Object> getJpaPropertyMap() { 206 return this.jpaPropertyMap; 207 } 208 209 /** 210 * Specify the (potentially vendor-specific) EntityManagerFactory interface 211 * that this EntityManagerFactory proxy is supposed to implement. 212 * <p>The default will be taken from the specific JpaVendorAdapter, if any, 213 * or set to the standard {@code javax.persistence.EntityManagerFactory} 214 * interface else. 215 * @see JpaVendorAdapter#getEntityManagerFactoryInterface() 216 */ 217 public void setEntityManagerFactoryInterface(Class<? extends EntityManagerFactory> emfInterface) { 218 this.entityManagerFactoryInterface = emfInterface; 219 } 220 221 /** 222 * Specify the (potentially vendor-specific) EntityManager interface 223 * that this factory's EntityManagers are supposed to implement. 224 * <p>The default will be taken from the specific JpaVendorAdapter, if any, 225 * or set to the standard {@code javax.persistence.EntityManager} 226 * interface else. 227 * @see JpaVendorAdapter#getEntityManagerInterface() 228 * @see EntityManagerFactoryInfo#getEntityManagerInterface() 229 */ 230 public void setEntityManagerInterface(Class<? extends EntityManager> emInterface) { 231 this.entityManagerInterface = emInterface; 232 } 233 234 @Override 235 public Class<? extends EntityManager> getEntityManagerInterface() { 236 return this.entityManagerInterface; 237 } 238 239 /** 240 * Specify the vendor-specific JpaDialect implementation to associate with 241 * this EntityManagerFactory. This will be exposed through the 242 * EntityManagerFactoryInfo interface, to be picked up as default dialect by 243 * accessors that intend to use JpaDialect functionality. 244 * @see EntityManagerFactoryInfo#getJpaDialect() 245 */ 246 public void setJpaDialect(JpaDialect jpaDialect) { 247 this.jpaDialect = jpaDialect; 248 } 249 250 @Override 251 public JpaDialect getJpaDialect() { 252 return this.jpaDialect; 253 } 254 255 /** 256 * Specify the JpaVendorAdapter implementation for the desired JPA provider, 257 * if any. This will initialize appropriate defaults for the given provider, 258 * such as persistence provider class and JpaDialect, unless locally 259 * overridden in this FactoryBean. 260 */ 261 public void setJpaVendorAdapter(JpaVendorAdapter jpaVendorAdapter) { 262 this.jpaVendorAdapter = jpaVendorAdapter; 263 } 264 265 /** 266 * Return the JpaVendorAdapter implementation for this EntityManagerFactory, 267 * or {@code null} if not known. 268 */ 269 public JpaVendorAdapter getJpaVendorAdapter() { 270 return this.jpaVendorAdapter; 271 } 272 273 /** 274 * Specify an asynchronous executor for background bootstrapping, 275 * e.g. a {@link org.springframework.core.task.SimpleAsyncTaskExecutor}. 276 * <p>{@code EntityManagerFactory} initialization will then switch into background 277 * bootstrap mode, with a {@code EntityManagerFactory} proxy immediately returned for 278 * injection purposes instead of waiting for the JPA provider's bootstrapping to complete. 279 * However, note that the first actual call to a {@code EntityManagerFactory} method will 280 * then block until the JPA provider's bootstrapping completed, if not ready by then. 281 * For maximum benefit, make sure to avoid early {@code EntityManagerFactory} calls 282 * in init methods of related beans, even for metadata introspection purposes. 283 * @since 4.3 284 */ 285 public void setBootstrapExecutor(AsyncTaskExecutor bootstrapExecutor) { 286 this.bootstrapExecutor = bootstrapExecutor; 287 } 288 289 /** 290 * Return the asynchronous executor for background bootstrapping, if any. 291 * @since 4.3 292 */ 293 public AsyncTaskExecutor getBootstrapExecutor() { 294 return this.bootstrapExecutor; 295 } 296 297 @Override 298 public void setBeanClassLoader(ClassLoader classLoader) { 299 this.beanClassLoader = classLoader; 300 } 301 302 @Override 303 public ClassLoader getBeanClassLoader() { 304 return this.beanClassLoader; 305 } 306 307 @Override 308 public void setBeanFactory(BeanFactory beanFactory) { 309 this.beanFactory = beanFactory; 310 } 311 312 @Override 313 public void setBeanName(String name) { 314 this.beanName = name; 315 } 316 317 318 @Override 319 public void afterPropertiesSet() throws PersistenceException { 320 JpaVendorAdapter jpaVendorAdapter = getJpaVendorAdapter(); 321 if (jpaVendorAdapter != null) { 322 if (this.persistenceProvider == null) { 323 this.persistenceProvider = jpaVendorAdapter.getPersistenceProvider(); 324 } 325 PersistenceUnitInfo pui = getPersistenceUnitInfo(); 326 Map<String, ?> vendorPropertyMap = null; 327 if (pui != null) { 328 try { 329 vendorPropertyMap = jpaVendorAdapter.getJpaPropertyMap(pui); 330 } 331 catch (AbstractMethodError err) { 332 // Spring 4.3.13 getJpaPropertyMap(PersistenceUnitInfo) not implemented 333 } 334 } 335 if (vendorPropertyMap == null) { 336 vendorPropertyMap = jpaVendorAdapter.getJpaPropertyMap(); 337 } 338 if (!CollectionUtils.isEmpty(vendorPropertyMap)) { 339 for (Map.Entry<String, ?> entry : vendorPropertyMap.entrySet()) { 340 if (!this.jpaPropertyMap.containsKey(entry.getKey())) { 341 this.jpaPropertyMap.put(entry.getKey(), entry.getValue()); 342 } 343 } 344 } 345 if (this.entityManagerFactoryInterface == null) { 346 this.entityManagerFactoryInterface = jpaVendorAdapter.getEntityManagerFactoryInterface(); 347 if (!ClassUtils.isVisible(this.entityManagerFactoryInterface, this.beanClassLoader)) { 348 this.entityManagerFactoryInterface = EntityManagerFactory.class; 349 } 350 } 351 if (this.entityManagerInterface == null) { 352 this.entityManagerInterface = jpaVendorAdapter.getEntityManagerInterface(); 353 if (!ClassUtils.isVisible(this.entityManagerInterface, this.beanClassLoader)) { 354 this.entityManagerInterface = EntityManager.class; 355 } 356 } 357 if (this.jpaDialect == null) { 358 this.jpaDialect = jpaVendorAdapter.getJpaDialect(); 359 } 360 } 361 362 if (this.bootstrapExecutor != null) { 363 this.nativeEntityManagerFactoryFuture = this.bootstrapExecutor.submit(new Callable<EntityManagerFactory>() { 364 @Override 365 public EntityManagerFactory call() { 366 return buildNativeEntityManagerFactory(); 367 } 368 }); 369 } 370 else { 371 this.nativeEntityManagerFactory = buildNativeEntityManagerFactory(); 372 } 373 374 // Wrap the EntityManagerFactory in a factory implementing all its interfaces. 375 // This allows interception of createEntityManager methods to return an 376 // application-managed EntityManager proxy that automatically joins 377 // existing transactions. 378 this.entityManagerFactory = createEntityManagerFactoryProxy(this.nativeEntityManagerFactory); 379 } 380 381 private EntityManagerFactory buildNativeEntityManagerFactory() { 382 EntityManagerFactory emf; 383 try { 384 emf = createNativeEntityManagerFactory(); 385 } 386 catch (PersistenceException ex) { 387 if (ex.getClass() == PersistenceException.class) { 388 // Plain PersistenceException wrapper for underlying exception? 389 // Make sure the nested exception message is properly exposed, 390 // along the lines of Spring's NestedRuntimeException.getMessage() 391 Throwable cause = ex.getCause(); 392 if (cause != null) { 393 String message = ex.getMessage(); 394 String causeString = cause.toString(); 395 if (!message.endsWith(causeString)) { 396 throw new PersistenceException(message + "; nested exception is " + causeString, cause); 397 } 398 } 399 } 400 throw ex; 401 } 402 403 if (emf == null) { 404 throw new IllegalStateException( 405 "JPA PersistenceProvider returned null EntityManagerFactory - check your JPA provider setup!"); 406 } 407 408 JpaVendorAdapter jpaVendorAdapter = getJpaVendorAdapter(); 409 if (jpaVendorAdapter != null) { 410 jpaVendorAdapter.postProcessEntityManagerFactory(emf); 411 } 412 413 if (logger.isInfoEnabled()) { 414 logger.info("Initialized JPA EntityManagerFactory for persistence unit '" + getPersistenceUnitName() + "'"); 415 } 416 return emf; 417 } 418 419 /** 420 * Create a proxy for the given {@link EntityManagerFactory}. We do this to be able to 421 * return a transaction-aware proxy for an application-managed {@link EntityManager}. 422 * @param emf the EntityManagerFactory as returned by the persistence provider, 423 * if initialized already 424 * @return the EntityManagerFactory proxy 425 */ 426 protected EntityManagerFactory createEntityManagerFactoryProxy(EntityManagerFactory emf) { 427 Set<Class<?>> ifcs = new LinkedHashSet<Class<?>>(); 428 Class<?> entityManagerFactoryInterface = this.entityManagerFactoryInterface; 429 if (entityManagerFactoryInterface != null) { 430 ifcs.add(entityManagerFactoryInterface); 431 } 432 else if (emf != null) { 433 ifcs.addAll(ClassUtils.getAllInterfacesForClassAsSet(emf.getClass(), this.beanClassLoader)); 434 } 435 else { 436 ifcs.add(EntityManagerFactory.class); 437 } 438 ifcs.add(EntityManagerFactoryInfo.class); 439 440 try { 441 return (EntityManagerFactory) Proxy.newProxyInstance(this.beanClassLoader, 442 ClassUtils.toClassArray(ifcs), new ManagedEntityManagerFactoryInvocationHandler(this)); 443 } 444 catch (IllegalArgumentException ex) { 445 if (entityManagerFactoryInterface != null) { 446 throw new IllegalStateException("EntityManagerFactory interface [" + entityManagerFactoryInterface + 447 "] seems to conflict with Spring's EntityManagerFactoryInfo mixin - consider resetting the "+ 448 "'entityManagerFactoryInterface' property to plain [javax.persistence.EntityManagerFactory]", ex); 449 } 450 else { 451 throw new IllegalStateException("Conflicting EntityManagerFactory interfaces - " + 452 "consider specifying the 'jpaVendorAdapter' or 'entityManagerFactoryInterface' property " + 453 "to select a specific EntityManagerFactory interface to proceed with", ex); 454 } 455 } 456 } 457 458 /** 459 * Delegate an incoming invocation from the proxy, dispatching to EntityManagerFactoryInfo 460 * or the native EntityManagerFactory accordingly. 461 */ 462 Object invokeProxyMethod(Method method, Object[] args) throws Throwable { 463 if (method.getDeclaringClass().isAssignableFrom(EntityManagerFactoryInfo.class)) { 464 return method.invoke(this, args); 465 } 466 else if (method.getName().equals("createEntityManager") && args != null && args.length > 0 && 467 args[0] != null && args[0].getClass().isEnum() && "SYNCHRONIZED".equals(args[0].toString())) { 468 // JPA 2.1's createEntityManager(SynchronizationType, Map) 469 // Redirect to plain createEntityManager and add synchronization semantics through Spring proxy 470 EntityManager rawEntityManager = (args.length > 1 ? 471 getNativeEntityManagerFactory().createEntityManager((Map<?, ?>) args[1]) : 472 getNativeEntityManagerFactory().createEntityManager()); 473 return ExtendedEntityManagerCreator.createApplicationManagedEntityManager(rawEntityManager, this, true); 474 } 475 476 // Look for Query arguments, primarily JPA 2.1's addNamedQuery(String, Query) 477 if (args != null) { 478 for (int i = 0; i < args.length; i++) { 479 Object arg = args[i]; 480 if (arg instanceof Query && Proxy.isProxyClass(arg.getClass())) { 481 // Assumably a Spring-generated proxy from SharedEntityManagerCreator: 482 // since we're passing it back to the native EntityManagerFactory, 483 // let's unwrap it to the original Query object from the provider. 484 try { 485 args[i] = ((Query) arg).unwrap(null); 486 } 487 catch (RuntimeException ex) { 488 // Ignore - simply proceed with given Query object then 489 } 490 } 491 } 492 } 493 494 // Standard delegation to the native factory, just post-processing EntityManager return values 495 Object retVal = method.invoke(getNativeEntityManagerFactory(), args); 496 if (retVal instanceof EntityManager) { 497 // Any other createEntityManager variant - expecting non-synchronized semantics 498 EntityManager rawEntityManager = (EntityManager) retVal; 499 retVal = ExtendedEntityManagerCreator.createApplicationManagedEntityManager(rawEntityManager, this, false); 500 } 501 return retVal; 502 } 503 504 /** 505 * Subclasses must implement this method to create the EntityManagerFactory 506 * that will be returned by the {@code getObject()} method. 507 * @return EntityManagerFactory instance returned by this FactoryBean 508 * @throws PersistenceException if the EntityManager cannot be created 509 */ 510 protected abstract EntityManagerFactory createNativeEntityManagerFactory() throws PersistenceException; 511 512 513 /** 514 * Implementation of the PersistenceExceptionTranslator interface, as 515 * autodetected by Spring's PersistenceExceptionTranslationPostProcessor. 516 * <p>Uses the dialect's conversion if possible; otherwise falls back to 517 * standard JPA exception conversion. 518 * @see org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor 519 * @see JpaDialect#translateExceptionIfPossible 520 * @see EntityManagerFactoryUtils#convertJpaAccessExceptionIfPossible 521 */ 522 @Override 523 public DataAccessException translateExceptionIfPossible(RuntimeException ex) { 524 JpaDialect jpaDialect = getJpaDialect(); 525 return (jpaDialect != null ? jpaDialect.translateExceptionIfPossible(ex) : 526 EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(ex)); 527 } 528 529 @Override 530 public EntityManagerFactory getNativeEntityManagerFactory() { 531 if (this.nativeEntityManagerFactory != null) { 532 return this.nativeEntityManagerFactory; 533 } 534 else { 535 try { 536 return this.nativeEntityManagerFactoryFuture.get(); 537 } 538 catch (InterruptedException ex) { 539 Thread.currentThread().interrupt(); 540 throw new IllegalStateException("Interrupted during initialization of native EntityManagerFactory", ex); 541 } 542 catch (ExecutionException ex) { 543 Throwable cause = ex.getCause(); 544 if (cause instanceof PersistenceException) { 545 // Rethrow a provider configuration exception (possibly with a nested cause) directly 546 throw (PersistenceException) cause; 547 } 548 throw new IllegalStateException("Failed to asynchronously initialize native EntityManagerFactory: " + 549 ex.getMessage(), cause); 550 } 551 } 552 } 553 554 @Override 555 public PersistenceUnitInfo getPersistenceUnitInfo() { 556 return null; 557 } 558 559 @Override 560 public DataSource getDataSource() { 561 return null; 562 } 563 564 565 /** 566 * Return the singleton EntityManagerFactory. 567 */ 568 @Override 569 public EntityManagerFactory getObject() { 570 return this.entityManagerFactory; 571 } 572 573 @Override 574 public Class<? extends EntityManagerFactory> getObjectType() { 575 return (this.entityManagerFactory != null ? this.entityManagerFactory.getClass() : EntityManagerFactory.class); 576 } 577 578 @Override 579 public boolean isSingleton() { 580 return true; 581 } 582 583 584 /** 585 * Close the EntityManagerFactory on bean factory shutdown. 586 */ 587 @Override 588 public void destroy() { 589 if (logger.isInfoEnabled()) { 590 logger.info("Closing JPA EntityManagerFactory for persistence unit '" + getPersistenceUnitName() + "'"); 591 } 592 this.entityManagerFactory.close(); 593 } 594 595 596 //--------------------------------------------------------------------- 597 // Serialization support 598 //--------------------------------------------------------------------- 599 600 private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { 601 throw new NotSerializableException("An EntityManagerFactoryBean itself is not deserializable - " + 602 "just a SerializedEntityManagerFactoryBeanReference is"); 603 } 604 605 protected Object writeReplace() throws ObjectStreamException { 606 if (this.beanFactory != null && this.beanName != null) { 607 return new SerializedEntityManagerFactoryBeanReference(this.beanFactory, this.beanName); 608 } 609 else { 610 throw new NotSerializableException("EntityManagerFactoryBean does not run within a BeanFactory"); 611 } 612 } 613 614 615 /** 616 * Minimal bean reference to the surrounding AbstractEntityManagerFactoryBean. 617 * Resolved to the actual AbstractEntityManagerFactoryBean instance on deserialization. 618 */ 619 @SuppressWarnings("serial") 620 private static class SerializedEntityManagerFactoryBeanReference implements Serializable { 621 622 private final BeanFactory beanFactory; 623 624 private final String lookupName; 625 626 public SerializedEntityManagerFactoryBeanReference(BeanFactory beanFactory, String beanName) { 627 this.beanFactory = beanFactory; 628 this.lookupName = BeanFactory.FACTORY_BEAN_PREFIX + beanName; 629 } 630 631 private Object readResolve() { 632 return this.beanFactory.getBean(this.lookupName, AbstractEntityManagerFactoryBean.class); 633 } 634 } 635 636 637 /** 638 * Dynamic proxy invocation handler for an {@link EntityManagerFactory}, returning a 639 * proxy {@link EntityManager} (if necessary) from {@code createEntityManager} methods. 640 */ 641 @SuppressWarnings("serial") 642 private static class ManagedEntityManagerFactoryInvocationHandler implements InvocationHandler, Serializable { 643 644 private final AbstractEntityManagerFactoryBean entityManagerFactoryBean; 645 646 public ManagedEntityManagerFactoryInvocationHandler(AbstractEntityManagerFactoryBean emfb) { 647 this.entityManagerFactoryBean = emfb; 648 } 649 650 @Override 651 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 652 try { 653 if (method.getName().equals("equals")) { 654 // Only consider equal when proxies are identical. 655 return (proxy == args[0]); 656 } 657 else if (method.getName().equals("hashCode")) { 658 // Use hashCode of EntityManagerFactory proxy. 659 return System.identityHashCode(proxy); 660 } 661 else if (method.getName().equals("unwrap")) { 662 // Handle JPA 2.1 unwrap method - could be a proxy match. 663 Class<?> targetClass = (Class<?>) args[0]; 664 if (targetClass == null) { 665 return this.entityManagerFactoryBean.getNativeEntityManagerFactory(); 666 } 667 else if (targetClass.isInstance(proxy)) { 668 return proxy; 669 } 670 } 671 return this.entityManagerFactoryBean.invokeProxyMethod(method, args); 672 } 673 catch (InvocationTargetException ex) { 674 throw ex.getTargetException(); 675 } 676 } 677 } 678 679}