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