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