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.support; 018 019import java.beans.PropertyDescriptor; 020import java.io.Serializable; 021import java.lang.reflect.AnnotatedElement; 022import java.lang.reflect.Member; 023import java.lang.reflect.Method; 024import java.lang.reflect.Modifier; 025import java.util.ArrayList; 026import java.util.Arrays; 027import java.util.List; 028import java.util.Map; 029import java.util.Properties; 030import java.util.concurrent.ConcurrentHashMap; 031 032import javax.persistence.EntityManager; 033import javax.persistence.EntityManagerFactory; 034import javax.persistence.PersistenceContext; 035import javax.persistence.PersistenceContextType; 036import javax.persistence.PersistenceProperty; 037import javax.persistence.PersistenceUnit; 038import javax.persistence.SynchronizationType; 039 040import org.springframework.beans.BeanUtils; 041import org.springframework.beans.PropertyValues; 042import org.springframework.beans.factory.BeanCreationException; 043import org.springframework.beans.factory.BeanFactory; 044import org.springframework.beans.factory.BeanFactoryAware; 045import org.springframework.beans.factory.ListableBeanFactory; 046import org.springframework.beans.factory.NoSuchBeanDefinitionException; 047import org.springframework.beans.factory.annotation.InjectionMetadata; 048import org.springframework.beans.factory.config.ConfigurableBeanFactory; 049import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; 050import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor; 051import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor; 052import org.springframework.beans.factory.config.NamedBeanHolder; 053import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor; 054import org.springframework.beans.factory.support.RootBeanDefinition; 055import org.springframework.core.BridgeMethodResolver; 056import org.springframework.core.Ordered; 057import org.springframework.core.PriorityOrdered; 058import org.springframework.core.annotation.AnnotationUtils; 059import org.springframework.jndi.JndiLocatorDelegate; 060import org.springframework.jndi.JndiTemplate; 061import org.springframework.lang.Nullable; 062import org.springframework.orm.jpa.EntityManagerFactoryInfo; 063import org.springframework.orm.jpa.EntityManagerFactoryUtils; 064import org.springframework.orm.jpa.EntityManagerProxy; 065import org.springframework.orm.jpa.ExtendedEntityManagerCreator; 066import org.springframework.orm.jpa.SharedEntityManagerCreator; 067import org.springframework.util.Assert; 068import org.springframework.util.ClassUtils; 069import org.springframework.util.ObjectUtils; 070import org.springframework.util.ReflectionUtils; 071import org.springframework.util.StringUtils; 072 073/** 074 * BeanPostProcessor that processes {@link javax.persistence.PersistenceUnit} 075 * and {@link javax.persistence.PersistenceContext} annotations, for injection of 076 * the corresponding JPA resources {@link javax.persistence.EntityManagerFactory} 077 * and {@link javax.persistence.EntityManager}. Any such annotated fields or methods 078 * in any Spring-managed object will automatically be injected. 079 * 080 * <p>This post-processor will inject sub-interfaces of {@code EntityManagerFactory} 081 * and {@code EntityManager} if the annotated fields or methods are declared as such. 082 * The actual type will be verified early, with the exception of a shared ("transactional") 083 * {@code EntityManager} reference, where type mismatches might be detected as late 084 * as on the first actual invocation. 085 * 086 * <p>Note: In the present implementation, PersistenceAnnotationBeanPostProcessor 087 * only supports {@code @PersistenceUnit} and {@code @PersistenceContext} 088 * with the "unitName" attribute, or no attribute at all (for the default unit). 089 * If those annotations are present with the "name" attribute at the class level, 090 * they will simply be ignored, since those only serve as deployment hint 091 * (as per the Java EE specification). 092 * 093 * <p>This post-processor can either obtain EntityManagerFactory beans defined 094 * in the Spring application context (the default), or obtain EntityManagerFactory 095 * references from JNDI ("persistence unit references"). In the bean case, 096 * the persistence unit name will be matched against the actual deployed unit, 097 * with the bean name used as fallback unit name if no deployed name found. 098 * Typically, Spring's {@link org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean} 099 * will be used for setting up such EntityManagerFactory beans. Alternatively, 100 * such beans may also be obtained from JNDI, e.g. using the {@code jee:jndi-lookup} 101 * XML configuration element (with the bean name matching the requested unit name). 102 * In both cases, the post-processor definition will look as simple as this: 103 * 104 * <pre class="code"> 105 * <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/></pre> 106 * 107 * In the JNDI case, specify the corresponding JNDI names in this post-processor's 108 * {@link #setPersistenceUnits "persistenceUnits" map}, typically with matching 109 * {@code persistence-unit-ref} entries in the Java EE deployment descriptor. 110 * By default, those names are considered as resource references (according to the 111 * Java EE resource-ref convention), located underneath the "java:comp/env/" namespace. 112 * For example: 113 * 114 * <pre class="code"> 115 * <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"> 116 * <property name="persistenceUnits"> 117 * <map/gt; 118 * <entry key="unit1" value="persistence/unit1"/> 119 * <entry key="unit2" value="persistence/unit2"/> 120 * </map/gt; 121 * </property> 122 * </bean></pre> 123 * 124 * In this case, the specified persistence units will always be resolved in JNDI 125 * rather than as Spring-defined beans. The entire persistence unit deployment, 126 * including the weaving of persistent classes, is then up to the Java EE server. 127 * Persistence contexts (i.e. EntityManager references) will be built based on 128 * those server-provided EntityManagerFactory references, using Spring's own 129 * transaction synchronization facilities for transactional EntityManager handling 130 * (typically with Spring's {@code @Transactional} annotation for demarcation 131 * and {@link org.springframework.transaction.jta.JtaTransactionManager} as backend). 132 * 133 * <p>If you prefer the Java EE server's own EntityManager handling, specify entries 134 * in this post-processor's {@link #setPersistenceContexts "persistenceContexts" map} 135 * (or {@link #setExtendedPersistenceContexts "extendedPersistenceContexts" map}, 136 * typically with matching {@code persistence-context-ref} entries in the 137 * Java EE deployment descriptor. For example: 138 * 139 * <pre class="code"> 140 * <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"> 141 * <property name="persistenceContexts"> 142 * <map/gt; 143 * <entry key="unit1" value="persistence/context1"/> 144 * <entry key="unit2" value="persistence/context2"/> 145 * </map/gt; 146 * </property> 147 * </bean></pre> 148 * 149 * If the application only obtains EntityManager references in the first place, 150 * this is all you need to specify. If you need EntityManagerFactory references 151 * as well, specify entries for both "persistenceUnits" and "persistenceContexts", 152 * pointing to matching JNDI locations. 153 * 154 * <p><b>NOTE: In general, do not inject EXTENDED EntityManagers into STATELESS beans, 155 * i.e. do not use {@code @PersistenceContext} with type {@code EXTENDED} in 156 * Spring beans defined with scope 'singleton' (Spring's default scope).</b> 157 * Extended EntityManagers are <i>not</i> thread-safe, hence they must not be used 158 * in concurrently accessed beans (which Spring-managed singletons usually are). 159 * 160 * <p>Note: A default PersistenceAnnotationBeanPostProcessor will be registered 161 * by the "context:annotation-config" and "context:component-scan" XML tags. 162 * Remove or turn off the default annotation configuration there if you intend 163 * to specify a custom PersistenceAnnotationBeanPostProcessor bean definition. 164 * 165 * @author Rod Johnson 166 * @author Juergen Hoeller 167 * @since 2.0 168 * @see javax.persistence.PersistenceUnit 169 * @see javax.persistence.PersistenceContext 170 */ 171@SuppressWarnings("serial") 172public class PersistenceAnnotationBeanPostProcessor 173 implements InstantiationAwareBeanPostProcessor, DestructionAwareBeanPostProcessor, 174 MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware, Serializable { 175 176 @Nullable 177 private Object jndiEnvironment; 178 179 private boolean resourceRef = true; 180 181 @Nullable 182 private transient Map<String, String> persistenceUnits; 183 184 @Nullable 185 private transient Map<String, String> persistenceContexts; 186 187 @Nullable 188 private transient Map<String, String> extendedPersistenceContexts; 189 190 private transient String defaultPersistenceUnitName = ""; 191 192 private int order = Ordered.LOWEST_PRECEDENCE - 4; 193 194 @Nullable 195 private transient ListableBeanFactory beanFactory; 196 197 private final transient Map<String, InjectionMetadata> injectionMetadataCache = new ConcurrentHashMap<>(256); 198 199 private final Map<Object, EntityManager> extendedEntityManagersToClose = new ConcurrentHashMap<>(16); 200 201 202 /** 203 * Set the JNDI template to use for JNDI lookups. 204 * @see org.springframework.jndi.JndiAccessor#setJndiTemplate 205 */ 206 public void setJndiTemplate(Object jndiTemplate) { 207 this.jndiEnvironment = jndiTemplate; 208 } 209 210 /** 211 * Set the JNDI environment to use for JNDI lookups. 212 * @see org.springframework.jndi.JndiAccessor#setJndiEnvironment 213 */ 214 public void setJndiEnvironment(Properties jndiEnvironment) { 215 this.jndiEnvironment = jndiEnvironment; 216 } 217 218 /** 219 * Set whether the lookup occurs in a Java EE container, i.e. if the prefix 220 * "java:comp/env/" needs to be added if the JNDI name doesn't already 221 * contain it. PersistenceAnnotationBeanPostProcessor's default is "true". 222 * @see org.springframework.jndi.JndiLocatorSupport#setResourceRef 223 */ 224 public void setResourceRef(boolean resourceRef) { 225 this.resourceRef = resourceRef; 226 } 227 228 /** 229 * Specify the persistence units for EntityManagerFactory lookups, 230 * as a Map from persistence unit name to persistence unit JNDI name 231 * (which needs to resolve to an EntityManagerFactory instance). 232 * <p>JNDI names specified here should refer to {@code persistence-unit-ref} 233 * entries in the Java EE deployment descriptor, matching the target persistence unit. 234 * <p>In case of no unit name specified in the annotation, the specified value 235 * for the {@link #setDefaultPersistenceUnitName default persistence unit} 236 * will be taken (by default, the value mapped to the empty String), 237 * or simply the single persistence unit if there is only one. 238 * <p>This is mainly intended for use in a Java EE environment, with all lookup 239 * driven by the standard JPA annotations, and all EntityManagerFactory 240 * references obtained from JNDI. No separate EntityManagerFactory bean 241 * definitions are necessary in such a scenario. 242 * <p>If no corresponding "persistenceContexts"/"extendedPersistenceContexts" 243 * are specified, {@code @PersistenceContext} will be resolved to 244 * EntityManagers built on top of the EntityManagerFactory defined here. 245 * Note that those will be Spring-managed EntityManagers, which implement 246 * transaction synchronization based on Spring's facilities. 247 * If you prefer the Java EE server's own EntityManager handling, 248 * specify corresponding "persistenceContexts"/"extendedPersistenceContexts". 249 */ 250 public void setPersistenceUnits(Map<String, String> persistenceUnits) { 251 this.persistenceUnits = persistenceUnits; 252 } 253 254 /** 255 * Specify the <i>transactional</i> persistence contexts for EntityManager lookups, 256 * as a Map from persistence unit name to persistence context JNDI name 257 * (which needs to resolve to an EntityManager instance). 258 * <p>JNDI names specified here should refer to {@code persistence-context-ref} 259 * entries in the Java EE deployment descriptors, matching the target persistence unit 260 * and being set up with persistence context type {@code Transaction}. 261 * <p>In case of no unit name specified in the annotation, the specified value 262 * for the {@link #setDefaultPersistenceUnitName default persistence unit} 263 * will be taken (by default, the value mapped to the empty String), 264 * or simply the single persistence unit if there is only one. 265 * <p>This is mainly intended for use in a Java EE environment, with all 266 * lookup driven by the standard JPA annotations, and all EntityManager 267 * references obtained from JNDI. No separate EntityManagerFactory bean 268 * definitions are necessary in such a scenario, and all EntityManager 269 * handling is done by the Java EE server itself. 270 */ 271 public void setPersistenceContexts(Map<String, String> persistenceContexts) { 272 this.persistenceContexts = persistenceContexts; 273 } 274 275 /** 276 * Specify the <i>extended</i> persistence contexts for EntityManager lookups, 277 * as a Map from persistence unit name to persistence context JNDI name 278 * (which needs to resolve to an EntityManager instance). 279 * <p>JNDI names specified here should refer to {@code persistence-context-ref} 280 * entries in the Java EE deployment descriptors, matching the target persistence unit 281 * and being set up with persistence context type {@code Extended}. 282 * <p>In case of no unit name specified in the annotation, the specified value 283 * for the {@link #setDefaultPersistenceUnitName default persistence unit} 284 * will be taken (by default, the value mapped to the empty String), 285 * or simply the single persistence unit if there is only one. 286 * <p>This is mainly intended for use in a Java EE environment, with all 287 * lookup driven by the standard JPA annotations, and all EntityManager 288 * references obtained from JNDI. No separate EntityManagerFactory bean 289 * definitions are necessary in such a scenario, and all EntityManager 290 * handling is done by the Java EE server itself. 291 */ 292 public void setExtendedPersistenceContexts(Map<String, String> extendedPersistenceContexts) { 293 this.extendedPersistenceContexts = extendedPersistenceContexts; 294 } 295 296 /** 297 * Specify the default persistence unit name, to be used in case 298 * of no unit name specified in an {@code @PersistenceUnit} / 299 * {@code @PersistenceContext} annotation. 300 * <p>This is mainly intended for lookups in the application context, 301 * indicating the target persistence unit name (typically matching 302 * the bean name), but also applies to lookups in the 303 * {@link #setPersistenceUnits "persistenceUnits"} / 304 * {@link #setPersistenceContexts "persistenceContexts"} / 305 * {@link #setExtendedPersistenceContexts "extendedPersistenceContexts"} map, 306 * avoiding the need for duplicated mappings for the empty String there. 307 * <p>Default is to check for a single EntityManagerFactory bean 308 * in the Spring application context, if any. If there are multiple 309 * such factories, either specify this default persistence unit name 310 * or explicitly refer to named persistence units in your annotations. 311 */ 312 public void setDefaultPersistenceUnitName(@Nullable String unitName) { 313 this.defaultPersistenceUnitName = (unitName != null ? unitName : ""); 314 } 315 316 public void setOrder(int order) { 317 this.order = order; 318 } 319 320 @Override 321 public int getOrder() { 322 return this.order; 323 } 324 325 @Override 326 public void setBeanFactory(BeanFactory beanFactory) { 327 if (beanFactory instanceof ListableBeanFactory) { 328 this.beanFactory = (ListableBeanFactory) beanFactory; 329 } 330 } 331 332 333 @Override 334 public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) { 335 InjectionMetadata metadata = findPersistenceMetadata(beanName, beanType, null); 336 metadata.checkConfigMembers(beanDefinition); 337 } 338 339 @Override 340 public void resetBeanDefinition(String beanName) { 341 this.injectionMetadataCache.remove(beanName); 342 } 343 344 @Override 345 public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) { 346 return null; 347 } 348 349 @Override 350 public boolean postProcessAfterInstantiation(Object bean, String beanName) { 351 return true; 352 } 353 354 @Override 355 public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) { 356 InjectionMetadata metadata = findPersistenceMetadata(beanName, bean.getClass(), pvs); 357 try { 358 metadata.inject(bean, beanName, pvs); 359 } 360 catch (Throwable ex) { 361 throw new BeanCreationException(beanName, "Injection of persistence dependencies failed", ex); 362 } 363 return pvs; 364 } 365 366 @Deprecated 367 @Override 368 public PropertyValues postProcessPropertyValues( 369 PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) { 370 371 return postProcessProperties(pvs, bean, beanName); 372 } 373 374 @Override 375 public Object postProcessBeforeInitialization(Object bean, String beanName) { 376 return bean; 377 } 378 379 @Override 380 public Object postProcessAfterInitialization(Object bean, String beanName) { 381 return bean; 382 } 383 384 @Override 385 public void postProcessBeforeDestruction(Object bean, String beanName) { 386 EntityManager emToClose = this.extendedEntityManagersToClose.remove(bean); 387 EntityManagerFactoryUtils.closeEntityManager(emToClose); 388 } 389 390 @Override 391 public boolean requiresDestruction(Object bean) { 392 return this.extendedEntityManagersToClose.containsKey(bean); 393 } 394 395 396 private InjectionMetadata findPersistenceMetadata(String beanName, final Class<?> clazz, @Nullable PropertyValues pvs) { 397 // Fall back to class name as cache key, for backwards compatibility with custom callers. 398 String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName()); 399 // Quick check on the concurrent map first, with minimal locking. 400 InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey); 401 if (InjectionMetadata.needsRefresh(metadata, clazz)) { 402 synchronized (this.injectionMetadataCache) { 403 metadata = this.injectionMetadataCache.get(cacheKey); 404 if (InjectionMetadata.needsRefresh(metadata, clazz)) { 405 if (metadata != null) { 406 metadata.clear(pvs); 407 } 408 metadata = buildPersistenceMetadata(clazz); 409 this.injectionMetadataCache.put(cacheKey, metadata); 410 } 411 } 412 } 413 return metadata; 414 } 415 416 private InjectionMetadata buildPersistenceMetadata(final Class<?> clazz) { 417 if (!AnnotationUtils.isCandidateClass(clazz, Arrays.asList(PersistenceContext.class, PersistenceUnit.class))) { 418 return InjectionMetadata.EMPTY; 419 } 420 421 List<InjectionMetadata.InjectedElement> elements = new ArrayList<>(); 422 Class<?> targetClass = clazz; 423 424 do { 425 final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>(); 426 427 ReflectionUtils.doWithLocalFields(targetClass, field -> { 428 if (field.isAnnotationPresent(PersistenceContext.class) || 429 field.isAnnotationPresent(PersistenceUnit.class)) { 430 if (Modifier.isStatic(field.getModifiers())) { 431 throw new IllegalStateException("Persistence annotations are not supported on static fields"); 432 } 433 currElements.add(new PersistenceElement(field, field, null)); 434 } 435 }); 436 437 ReflectionUtils.doWithLocalMethods(targetClass, method -> { 438 Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); 439 if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) { 440 return; 441 } 442 if ((bridgedMethod.isAnnotationPresent(PersistenceContext.class) || 443 bridgedMethod.isAnnotationPresent(PersistenceUnit.class)) && 444 method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) { 445 if (Modifier.isStatic(method.getModifiers())) { 446 throw new IllegalStateException("Persistence annotations are not supported on static methods"); 447 } 448 if (method.getParameterCount() != 1) { 449 throw new IllegalStateException("Persistence annotation requires a single-arg method: " + method); 450 } 451 PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz); 452 currElements.add(new PersistenceElement(method, bridgedMethod, pd)); 453 } 454 }); 455 456 elements.addAll(0, currElements); 457 targetClass = targetClass.getSuperclass(); 458 } 459 while (targetClass != null && targetClass != Object.class); 460 461 return InjectionMetadata.forElements(elements, clazz); 462 } 463 464 /** 465 * Return a specified persistence unit for the given unit name, 466 * as defined through the "persistenceUnits" map. 467 * @param unitName the name of the persistence unit 468 * @return the corresponding EntityManagerFactory, 469 * or {@code null} if none found 470 * @see #setPersistenceUnits 471 */ 472 @Nullable 473 protected EntityManagerFactory getPersistenceUnit(@Nullable String unitName) { 474 if (this.persistenceUnits != null) { 475 String unitNameForLookup = (unitName != null ? unitName : ""); 476 if (unitNameForLookup.isEmpty()) { 477 unitNameForLookup = this.defaultPersistenceUnitName; 478 } 479 String jndiName = this.persistenceUnits.get(unitNameForLookup); 480 if (jndiName == null && unitNameForLookup.isEmpty() && this.persistenceUnits.size() == 1) { 481 jndiName = this.persistenceUnits.values().iterator().next(); 482 } 483 if (jndiName != null) { 484 try { 485 return lookup(jndiName, EntityManagerFactory.class); 486 } 487 catch (Exception ex) { 488 throw new IllegalStateException("Could not obtain EntityManagerFactory [" + jndiName + "] from JNDI", ex); 489 } 490 } 491 } 492 return null; 493 } 494 495 /** 496 * Return a specified persistence context for the given unit name, as defined 497 * through the "persistenceContexts" (or "extendedPersistenceContexts") map. 498 * @param unitName the name of the persistence unit 499 * @param extended whether to obtain an extended persistence context 500 * @return the corresponding EntityManager, or {@code null} if none found 501 * @see #setPersistenceContexts 502 * @see #setExtendedPersistenceContexts 503 */ 504 @Nullable 505 protected EntityManager getPersistenceContext(@Nullable String unitName, boolean extended) { 506 Map<String, String> contexts = (extended ? this.extendedPersistenceContexts : this.persistenceContexts); 507 if (contexts != null) { 508 String unitNameForLookup = (unitName != null ? unitName : ""); 509 if (unitNameForLookup.isEmpty()) { 510 unitNameForLookup = this.defaultPersistenceUnitName; 511 } 512 String jndiName = contexts.get(unitNameForLookup); 513 if (jndiName == null && unitNameForLookup.isEmpty() && contexts.size() == 1) { 514 jndiName = contexts.values().iterator().next(); 515 } 516 if (jndiName != null) { 517 try { 518 return lookup(jndiName, EntityManager.class); 519 } 520 catch (Exception ex) { 521 throw new IllegalStateException("Could not obtain EntityManager [" + jndiName + "] from JNDI", ex); 522 } 523 } 524 } 525 return null; 526 } 527 528 /** 529 * Find an EntityManagerFactory with the given name in the current Spring 530 * application context, falling back to a single default EntityManagerFactory 531 * (if any) in case of no unit name specified. 532 * @param unitName the name of the persistence unit (may be {@code null} or empty) 533 * @param requestingBeanName the name of the requesting bean 534 * @return the EntityManagerFactory 535 * @throws NoSuchBeanDefinitionException if there is no such EntityManagerFactory in the context 536 */ 537 protected EntityManagerFactory findEntityManagerFactory(@Nullable String unitName, @Nullable String requestingBeanName) 538 throws NoSuchBeanDefinitionException { 539 540 String unitNameForLookup = (unitName != null ? unitName : ""); 541 if (unitNameForLookup.isEmpty()) { 542 unitNameForLookup = this.defaultPersistenceUnitName; 543 } 544 if (!unitNameForLookup.isEmpty()) { 545 return findNamedEntityManagerFactory(unitNameForLookup, requestingBeanName); 546 } 547 else { 548 return findDefaultEntityManagerFactory(requestingBeanName); 549 } 550 } 551 552 /** 553 * Find an EntityManagerFactory with the given name in the current 554 * Spring application context. 555 * @param unitName the name of the persistence unit (never empty) 556 * @param requestingBeanName the name of the requesting bean 557 * @return the EntityManagerFactory 558 * @throws NoSuchBeanDefinitionException if there is no such EntityManagerFactory in the context 559 */ 560 protected EntityManagerFactory findNamedEntityManagerFactory(String unitName, @Nullable String requestingBeanName) 561 throws NoSuchBeanDefinitionException { 562 563 Assert.state(this.beanFactory != null, "ListableBeanFactory required for EntityManagerFactory bean lookup"); 564 565 EntityManagerFactory emf = EntityManagerFactoryUtils.findEntityManagerFactory(this.beanFactory, unitName); 566 if (requestingBeanName != null && this.beanFactory instanceof ConfigurableBeanFactory) { 567 ((ConfigurableBeanFactory) this.beanFactory).registerDependentBean(unitName, requestingBeanName); 568 } 569 return emf; 570 } 571 572 /** 573 * Find a single default EntityManagerFactory in the Spring application context. 574 * @return the default EntityManagerFactory 575 * @throws NoSuchBeanDefinitionException if there is no single EntityManagerFactory in the context 576 */ 577 protected EntityManagerFactory findDefaultEntityManagerFactory(@Nullable String requestingBeanName) 578 throws NoSuchBeanDefinitionException { 579 580 Assert.state(this.beanFactory != null, "ListableBeanFactory required for EntityManagerFactory bean lookup"); 581 582 if (this.beanFactory instanceof ConfigurableListableBeanFactory) { 583 // Fancy variant with dependency registration 584 ConfigurableListableBeanFactory clbf = (ConfigurableListableBeanFactory) this.beanFactory; 585 NamedBeanHolder<EntityManagerFactory> emfHolder = clbf.resolveNamedBean(EntityManagerFactory.class); 586 if (requestingBeanName != null) { 587 clbf.registerDependentBean(emfHolder.getBeanName(), requestingBeanName); 588 } 589 return emfHolder.getBeanInstance(); 590 } 591 else { 592 // Plain variant: just find a default bean 593 return this.beanFactory.getBean(EntityManagerFactory.class); 594 } 595 } 596 597 /** 598 * Perform a JNDI lookup for the given resource by name. 599 * <p>Called for EntityManagerFactory and EntityManager lookup 600 * when JNDI names are mapped for specific persistence units. 601 * @param jndiName the JNDI name to look up 602 * @param requiredType the required type of the object 603 * @return the obtained object 604 * @throws Exception if the JNDI lookup failed 605 */ 606 protected <T> T lookup(String jndiName, Class<T> requiredType) throws Exception { 607 return new LocatorDelegate().lookup(jndiName, requiredType); 608 } 609 610 611 /** 612 * Separate inner class to isolate the JNDI API dependency 613 * (for compatibility with Google App Engine's API white list). 614 */ 615 private class LocatorDelegate { 616 617 public <T> T lookup(String jndiName, Class<T> requiredType) throws Exception { 618 JndiLocatorDelegate locator = new JndiLocatorDelegate(); 619 if (jndiEnvironment instanceof JndiTemplate) { 620 locator.setJndiTemplate((JndiTemplate) jndiEnvironment); 621 } 622 else if (jndiEnvironment instanceof Properties) { 623 locator.setJndiEnvironment((Properties) jndiEnvironment); 624 } 625 else if (jndiEnvironment != null) { 626 throw new IllegalStateException("Illegal 'jndiEnvironment' type: " + jndiEnvironment.getClass()); 627 } 628 locator.setResourceRef(resourceRef); 629 return locator.lookup(jndiName, requiredType); 630 } 631 } 632 633 634 /** 635 * Class representing injection information about an annotated field 636 * or setter method. 637 */ 638 private class PersistenceElement extends InjectionMetadata.InjectedElement { 639 640 private final String unitName; 641 642 @Nullable 643 private PersistenceContextType type; 644 645 private boolean synchronizedWithTransaction = false; 646 647 @Nullable 648 private Properties properties; 649 650 public PersistenceElement(Member member, AnnotatedElement ae, @Nullable PropertyDescriptor pd) { 651 super(member, pd); 652 PersistenceContext pc = ae.getAnnotation(PersistenceContext.class); 653 PersistenceUnit pu = ae.getAnnotation(PersistenceUnit.class); 654 Class<?> resourceType = EntityManager.class; 655 if (pc != null) { 656 if (pu != null) { 657 throw new IllegalStateException("Member may only be annotated with either " + 658 "@PersistenceContext or @PersistenceUnit, not both: " + member); 659 } 660 Properties properties = null; 661 PersistenceProperty[] pps = pc.properties(); 662 if (!ObjectUtils.isEmpty(pps)) { 663 properties = new Properties(); 664 for (PersistenceProperty pp : pps) { 665 properties.setProperty(pp.name(), pp.value()); 666 } 667 } 668 this.unitName = pc.unitName(); 669 this.type = pc.type(); 670 this.synchronizedWithTransaction = SynchronizationType.SYNCHRONIZED.equals(pc.synchronization()); 671 this.properties = properties; 672 } 673 else { 674 resourceType = EntityManagerFactory.class; 675 this.unitName = pu.unitName(); 676 } 677 checkResourceType(resourceType); 678 } 679 680 /** 681 * Resolve the object against the application context. 682 */ 683 @Override 684 protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) { 685 // Resolves to EntityManagerFactory or EntityManager. 686 if (this.type != null) { 687 return (this.type == PersistenceContextType.EXTENDED ? 688 resolveExtendedEntityManager(target, requestingBeanName) : 689 resolveEntityManager(requestingBeanName)); 690 } 691 else { 692 // OK, so we need an EntityManagerFactory... 693 return resolveEntityManagerFactory(requestingBeanName); 694 } 695 } 696 697 private EntityManagerFactory resolveEntityManagerFactory(@Nullable String requestingBeanName) { 698 // Obtain EntityManagerFactory from JNDI? 699 EntityManagerFactory emf = getPersistenceUnit(this.unitName); 700 if (emf == null) { 701 // Need to search for EntityManagerFactory beans. 702 emf = findEntityManagerFactory(this.unitName, requestingBeanName); 703 } 704 return emf; 705 } 706 707 private EntityManager resolveEntityManager(@Nullable String requestingBeanName) { 708 // Obtain EntityManager reference from JNDI? 709 EntityManager em = getPersistenceContext(this.unitName, false); 710 if (em == null) { 711 // No pre-built EntityManager found -> build one based on factory. 712 // Obtain EntityManagerFactory from JNDI? 713 EntityManagerFactory emf = getPersistenceUnit(this.unitName); 714 if (emf == null) { 715 // Need to search for EntityManagerFactory beans. 716 emf = findEntityManagerFactory(this.unitName, requestingBeanName); 717 } 718 // Inject a shared transactional EntityManager proxy. 719 if (emf instanceof EntityManagerFactoryInfo && 720 ((EntityManagerFactoryInfo) emf).getEntityManagerInterface() != null) { 721 // Create EntityManager based on the info's vendor-specific type 722 // (which might be more specific than the field's type). 723 em = SharedEntityManagerCreator.createSharedEntityManager( 724 emf, this.properties, this.synchronizedWithTransaction); 725 } 726 else { 727 // Create EntityManager based on the field's type. 728 em = SharedEntityManagerCreator.createSharedEntityManager( 729 emf, this.properties, this.synchronizedWithTransaction, getResourceType()); 730 } 731 } 732 return em; 733 } 734 735 private EntityManager resolveExtendedEntityManager(Object target, @Nullable String requestingBeanName) { 736 // Obtain EntityManager reference from JNDI? 737 EntityManager em = getPersistenceContext(this.unitName, true); 738 if (em == null) { 739 // No pre-built EntityManager found -> build one based on factory. 740 // Obtain EntityManagerFactory from JNDI? 741 EntityManagerFactory emf = getPersistenceUnit(this.unitName); 742 if (emf == null) { 743 // Need to search for EntityManagerFactory beans. 744 emf = findEntityManagerFactory(this.unitName, requestingBeanName); 745 } 746 // Inject a container-managed extended EntityManager. 747 em = ExtendedEntityManagerCreator.createContainerManagedEntityManager( 748 emf, this.properties, this.synchronizedWithTransaction); 749 } 750 if (em instanceof EntityManagerProxy && beanFactory != null && requestingBeanName != null && 751 beanFactory.containsBean(requestingBeanName) && !beanFactory.isPrototype(requestingBeanName)) { 752 extendedEntityManagersToClose.put(target, ((EntityManagerProxy) em).getTargetEntityManager()); 753 } 754 return em; 755 } 756 } 757 758}