Java tutorial
/* * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.orm.jpa.support; import java.beans.PropertyDescriptor; import java.io.Serializable; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Member; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.concurrent.ConcurrentHashMap; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.PersistenceContext; import javax.persistence.PersistenceContextType; import javax.persistence.PersistenceProperty; import javax.persistence.PersistenceUnit; import javax.persistence.SynchronizationType; import org.springframework.beans.BeanUtils; import org.springframework.beans.PropertyValues; import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.annotation.InjectionMetadata; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor; import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor; import org.springframework.beans.factory.config.NamedBeanHolder; import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.core.BridgeMethodResolver; import org.springframework.core.Ordered; import org.springframework.core.PriorityOrdered; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.jndi.JndiLocatorDelegate; import org.springframework.jndi.JndiTemplate; import org.springframework.lang.Nullable; import org.springframework.orm.jpa.EntityManagerFactoryInfo; import org.springframework.orm.jpa.EntityManagerFactoryUtils; import org.springframework.orm.jpa.EntityManagerProxy; import org.springframework.orm.jpa.ExtendedEntityManagerCreator; import org.springframework.orm.jpa.SharedEntityManagerCreator; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; /** * BeanPostProcessor that processes {@link javax.persistence.PersistenceUnit} * and {@link javax.persistence.PersistenceContext} annotations, for injection of * the corresponding JPA resources {@link javax.persistence.EntityManagerFactory} * and {@link javax.persistence.EntityManager}. Any such annotated fields or methods * in any Spring-managed object will automatically be injected. * * <p>This post-processor will inject sub-interfaces of {@code EntityManagerFactory} * and {@code EntityManager} if the annotated fields or methods are declared as such. * The actual type will be verified early, with the exception of a shared ("transactional") * {@code EntityManager} reference, where type mismatches might be detected as late * as on the first actual invocation. * * <p>Note: In the present implementation, PersistenceAnnotationBeanPostProcessor * only supports {@code @PersistenceUnit} and {@code @PersistenceContext} * with the "unitName" attribute, or no attribute at all (for the default unit). * If those annotations are present with the "name" attribute at the class level, * they will simply be ignored, since those only serve as deployment hint * (as per the Java EE specification). * * <p>This post-processor can either obtain EntityManagerFactory beans defined * in the Spring application context (the default), or obtain EntityManagerFactory * references from JNDI ("persistence unit references"). In the bean case, * the persistence unit name will be matched against the actual deployed unit, * with the bean name used as fallback unit name if no deployed name found. * Typically, Spring's {@link org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean} * will be used for setting up such EntityManagerFactory beans. Alternatively, * such beans may also be obtained from JNDI, e.g. using the {@code jee:jndi-lookup} * XML configuration element (with the bean name matching the requested unit name). * In both cases, the post-processor definition will look as simple as this: * * <pre class="code"> * <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/></pre> * * In the JNDI case, specify the corresponding JNDI names in this post-processor's * {@link #setPersistenceUnits "persistenceUnits" map}, typically with matching * {@code persistence-unit-ref} entries in the Java EE deployment descriptor. * By default, those names are considered as resource references (according to the * Java EE resource-ref convention), located underneath the "java:comp/env/" namespace. * For example: * * <pre class="code"> * <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"> * <property name="persistenceUnits"> * <map/gt; * <entry key="unit1" value="persistence/unit1"/> * <entry key="unit2" value="persistence/unit2"/> * </map/gt; * </property> * </bean></pre> * * In this case, the specified persistence units will always be resolved in JNDI * rather than as Spring-defined beans. The entire persistence unit deployment, * including the weaving of persistent classes, is then up to the Java EE server. * Persistence contexts (i.e. EntityManager references) will be built based on * those server-provided EntityManagerFactory references, using Spring's own * transaction synchronization facilities for transactional EntityManager handling * (typically with Spring's {@code @Transactional} annotation for demarcation * and {@link org.springframework.transaction.jta.JtaTransactionManager} as backend). * * <p>If you prefer the Java EE server's own EntityManager handling, specify entries * in this post-processor's {@link #setPersistenceContexts "persistenceContexts" map} * (or {@link #setExtendedPersistenceContexts "extendedPersistenceContexts" map}, * typically with matching {@code persistence-context-ref} entries in the * Java EE deployment descriptor. For example: * * <pre class="code"> * <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"> * <property name="persistenceContexts"> * <map/gt; * <entry key="unit1" value="persistence/context1"/> * <entry key="unit2" value="persistence/context2"/> * </map/gt; * </property> * </bean></pre> * * If the application only obtains EntityManager references in the first place, * this is all you need to specify. If you need EntityManagerFactory references * as well, specify entries for both "persistenceUnits" and "persistenceContexts", * pointing to matching JNDI locations. * * <p><b>NOTE: In general, do not inject EXTENDED EntityManagers into STATELESS beans, * i.e. do not use {@code @PersistenceContext} with type {@code EXTENDED} in * Spring beans defined with scope 'singleton' (Spring's default scope).</b> * Extended EntityManagers are <i>not</i> thread-safe, hence they must not be used * in concurrently accessed beans (which Spring-managed singletons usually are). * * <p>Note: A default PersistenceAnnotationBeanPostProcessor will be registered * by the "context:annotation-config" and "context:component-scan" XML tags. * Remove or turn off the default annotation configuration there if you intend * to specify a custom PersistenceAnnotationBeanPostProcessor bean definition. * * @author Rod Johnson * @author Juergen Hoeller * @since 2.0 * @see javax.persistence.PersistenceUnit * @see javax.persistence.PersistenceContext */ @SuppressWarnings("serial") public class PersistenceAnnotationBeanPostProcessor implements InstantiationAwareBeanPostProcessor, DestructionAwareBeanPostProcessor, MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware, Serializable { @Nullable private Object jndiEnvironment; private boolean resourceRef = true; @Nullable private transient Map<String, String> persistenceUnits; @Nullable private transient Map<String, String> persistenceContexts; @Nullable private transient Map<String, String> extendedPersistenceContexts; private transient String defaultPersistenceUnitName = ""; private int order = Ordered.LOWEST_PRECEDENCE - 4; @Nullable private transient ListableBeanFactory beanFactory; private final transient Map<String, InjectionMetadata> injectionMetadataCache = new ConcurrentHashMap<>(256); private final Map<Object, EntityManager> extendedEntityManagersToClose = new ConcurrentHashMap<>(16); /** * Set the JNDI template to use for JNDI lookups. * @see org.springframework.jndi.JndiAccessor#setJndiTemplate */ public void setJndiTemplate(Object jndiTemplate) { this.jndiEnvironment = jndiTemplate; } /** * Set the JNDI environment to use for JNDI lookups. * @see org.springframework.jndi.JndiAccessor#setJndiEnvironment */ public void setJndiEnvironment(Properties jndiEnvironment) { this.jndiEnvironment = jndiEnvironment; } /** * Set whether the lookup occurs in a Java EE container, i.e. if the prefix * "java:comp/env/" needs to be added if the JNDI name doesn't already * contain it. PersistenceAnnotationBeanPostProcessor's default is "true". * @see org.springframework.jndi.JndiLocatorSupport#setResourceRef */ public void setResourceRef(boolean resourceRef) { this.resourceRef = resourceRef; } /** * Specify the persistence units for EntityManagerFactory lookups, * as a Map from persistence unit name to persistence unit JNDI name * (which needs to resolve to an EntityManagerFactory instance). * <p>JNDI names specified here should refer to {@code persistence-unit-ref} * entries in the Java EE deployment descriptor, matching the target persistence unit. * <p>In case of no unit name specified in the annotation, the specified value * for the {@link #setDefaultPersistenceUnitName default persistence unit} * will be taken (by default, the value mapped to the empty String), * or simply the single persistence unit if there is only one. * <p>This is mainly intended for use in a Java EE environment, with all lookup * driven by the standard JPA annotations, and all EntityManagerFactory * references obtained from JNDI. No separate EntityManagerFactory bean * definitions are necessary in such a scenario. * <p>If no corresponding "persistenceContexts"/"extendedPersistenceContexts" * are specified, {@code @PersistenceContext} will be resolved to * EntityManagers built on top of the EntityManagerFactory defined here. * Note that those will be Spring-managed EntityManagers, which implement * transaction synchronization based on Spring's facilities. * If you prefer the Java EE server's own EntityManager handling, * specify corresponding "persistenceContexts"/"extendedPersistenceContexts". */ public void setPersistenceUnits(Map<String, String> persistenceUnits) { this.persistenceUnits = persistenceUnits; } /** * Specify the <i>transactional</i> persistence contexts for EntityManager lookups, * as a Map from persistence unit name to persistence context JNDI name * (which needs to resolve to an EntityManager instance). * <p>JNDI names specified here should refer to {@code persistence-context-ref} * entries in the Java EE deployment descriptors, matching the target persistence unit * and being set up with persistence context type {@code Transaction}. * <p>In case of no unit name specified in the annotation, the specified value * for the {@link #setDefaultPersistenceUnitName default persistence unit} * will be taken (by default, the value mapped to the empty String), * or simply the single persistence unit if there is only one. * <p>This is mainly intended for use in a Java EE environment, with all * lookup driven by the standard JPA annotations, and all EntityManager * references obtained from JNDI. No separate EntityManagerFactory bean * definitions are necessary in such a scenario, and all EntityManager * handling is done by the Java EE server itself. */ public void setPersistenceContexts(Map<String, String> persistenceContexts) { this.persistenceContexts = persistenceContexts; } /** * Specify the <i>extended</i> persistence contexts for EntityManager lookups, * as a Map from persistence unit name to persistence context JNDI name * (which needs to resolve to an EntityManager instance). * <p>JNDI names specified here should refer to {@code persistence-context-ref} * entries in the Java EE deployment descriptors, matching the target persistence unit * and being set up with persistence context type {@code Extended}. * <p>In case of no unit name specified in the annotation, the specified value * for the {@link #setDefaultPersistenceUnitName default persistence unit} * will be taken (by default, the value mapped to the empty String), * or simply the single persistence unit if there is only one. * <p>This is mainly intended for use in a Java EE environment, with all * lookup driven by the standard JPA annotations, and all EntityManager * references obtained from JNDI. No separate EntityManagerFactory bean * definitions are necessary in such a scenario, and all EntityManager * handling is done by the Java EE server itself. */ public void setExtendedPersistenceContexts(Map<String, String> extendedPersistenceContexts) { this.extendedPersistenceContexts = extendedPersistenceContexts; } /** * Specify the default persistence unit name, to be used in case * of no unit name specified in an {@code @PersistenceUnit} / * {@code @PersistenceContext} annotation. * <p>This is mainly intended for lookups in the application context, * indicating the target persistence unit name (typically matching * the bean name), but also applies to lookups in the * {@link #setPersistenceUnits "persistenceUnits"} / * {@link #setPersistenceContexts "persistenceContexts"} / * {@link #setExtendedPersistenceContexts "extendedPersistenceContexts"} map, * avoiding the need for duplicated mappings for the empty String there. * <p>Default is to check for a single EntityManagerFactory bean * in the Spring application context, if any. If there are multiple * such factories, either specify this default persistence unit name * or explicitly refer to named persistence units in your annotations. */ public void setDefaultPersistenceUnitName(@Nullable String unitName) { this.defaultPersistenceUnitName = (unitName != null ? unitName : ""); } public void setOrder(int order) { this.order = order; } @Override public int getOrder() { return this.order; } @Override public void setBeanFactory(BeanFactory beanFactory) { if (beanFactory instanceof ListableBeanFactory) { this.beanFactory = (ListableBeanFactory) beanFactory; } } @Override public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) { InjectionMetadata metadata = findPersistenceMetadata(beanName, beanType, null); metadata.checkConfigMembers(beanDefinition); } @Override public void resetBeanDefinition(String beanName) { this.injectionMetadataCache.remove(beanName); } @Override public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) { return null; } @Override public boolean postProcessAfterInstantiation(Object bean, String beanName) { return true; } @Override public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) { InjectionMetadata metadata = findPersistenceMetadata(beanName, bean.getClass(), pvs); try { metadata.inject(bean, beanName, pvs); } catch (Throwable ex) { throw new BeanCreationException(beanName, "Injection of persistence dependencies failed", ex); } return pvs; } @Deprecated @Override public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) { return postProcessProperties(pvs, bean, beanName); } @Override public Object postProcessBeforeInitialization(Object bean, String beanName) { return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) { return bean; } @Override public void postProcessBeforeDestruction(Object bean, String beanName) { EntityManager emToClose = this.extendedEntityManagersToClose.remove(bean); EntityManagerFactoryUtils.closeEntityManager(emToClose); } @Override public boolean requiresDestruction(Object bean) { return this.extendedEntityManagersToClose.containsKey(bean); } private InjectionMetadata findPersistenceMetadata(String beanName, final Class<?> clazz, @Nullable PropertyValues pvs) { // Fall back to class name as cache key, for backwards compatibility with custom callers. String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName()); // Quick check on the concurrent map first, with minimal locking. InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey); if (InjectionMetadata.needsRefresh(metadata, clazz)) { synchronized (this.injectionMetadataCache) { metadata = this.injectionMetadataCache.get(cacheKey); if (InjectionMetadata.needsRefresh(metadata, clazz)) { if (metadata != null) { metadata.clear(pvs); } metadata = buildPersistenceMetadata(clazz); this.injectionMetadataCache.put(cacheKey, metadata); } } } return metadata; } private InjectionMetadata buildPersistenceMetadata(final Class<?> clazz) { if (!AnnotationUtils.isCandidateClass(clazz, Arrays.asList(PersistenceContext.class, PersistenceUnit.class))) { return InjectionMetadata.EMPTY; } List<InjectionMetadata.InjectedElement> elements = new ArrayList<>(); Class<?> targetClass = clazz; do { final LinkedList<InjectionMetadata.InjectedElement> currElements = new LinkedList<>(); ReflectionUtils.doWithLocalFields(targetClass, field -> { if (field.isAnnotationPresent(PersistenceContext.class) || field.isAnnotationPresent(PersistenceUnit.class)) { if (Modifier.isStatic(field.getModifiers())) { throw new IllegalStateException( "Persistence annotations are not supported on static fields"); } currElements.add(new PersistenceElement(field, field, null)); } }); ReflectionUtils.doWithLocalMethods(targetClass, method -> { Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) { return; } if ((bridgedMethod.isAnnotationPresent(PersistenceContext.class) || bridgedMethod.isAnnotationPresent(PersistenceUnit.class)) && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) { if (Modifier.isStatic(method.getModifiers())) { throw new IllegalStateException( "Persistence annotations are not supported on static methods"); } if (method.getParameterCount() != 1) { throw new IllegalStateException( "Persistence annotation requires a single-arg method: " + method); } PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz); currElements.add(new PersistenceElement(method, bridgedMethod, pd)); } }); elements.addAll(0, currElements); targetClass = targetClass.getSuperclass(); } while (targetClass != null && targetClass != Object.class); return InjectionMetadata.forElements(elements, clazz); } /** * Return a specified persistence unit for the given unit name, * as defined through the "persistenceUnits" map. * @param unitName the name of the persistence unit * @return the corresponding EntityManagerFactory, * or {@code null} if none found * @see #setPersistenceUnits */ @Nullable protected EntityManagerFactory getPersistenceUnit(@Nullable String unitName) { if (this.persistenceUnits != null) { String unitNameForLookup = (unitName != null ? unitName : ""); if (unitNameForLookup.isEmpty()) { unitNameForLookup = this.defaultPersistenceUnitName; } String jndiName = this.persistenceUnits.get(unitNameForLookup); if (jndiName == null && unitNameForLookup.isEmpty() && this.persistenceUnits.size() == 1) { jndiName = this.persistenceUnits.values().iterator().next(); } if (jndiName != null) { try { return lookup(jndiName, EntityManagerFactory.class); } catch (Exception ex) { throw new IllegalStateException( "Could not obtain EntityManagerFactory [" + jndiName + "] from JNDI", ex); } } } return null; } /** * Return a specified persistence context for the given unit name, as defined * through the "persistenceContexts" (or "extendedPersistenceContexts") map. * @param unitName the name of the persistence unit * @param extended whether to obtain an extended persistence context * @return the corresponding EntityManager, or {@code null} if none found * @see #setPersistenceContexts * @see #setExtendedPersistenceContexts */ @Nullable protected EntityManager getPersistenceContext(@Nullable String unitName, boolean extended) { Map<String, String> contexts = (extended ? this.extendedPersistenceContexts : this.persistenceContexts); if (contexts != null) { String unitNameForLookup = (unitName != null ? unitName : ""); if (unitNameForLookup.isEmpty()) { unitNameForLookup = this.defaultPersistenceUnitName; } String jndiName = contexts.get(unitNameForLookup); if (jndiName == null && unitNameForLookup.isEmpty() && contexts.size() == 1) { jndiName = contexts.values().iterator().next(); } if (jndiName != null) { try { return lookup(jndiName, EntityManager.class); } catch (Exception ex) { throw new IllegalStateException("Could not obtain EntityManager [" + jndiName + "] from JNDI", ex); } } } return null; } /** * Find an EntityManagerFactory with the given name in the current Spring * application context, falling back to a single default EntityManagerFactory * (if any) in case of no unit name specified. * @param unitName the name of the persistence unit (may be {@code null} or empty) * @param requestingBeanName the name of the requesting bean * @return the EntityManagerFactory * @throws NoSuchBeanDefinitionException if there is no such EntityManagerFactory in the context */ protected EntityManagerFactory findEntityManagerFactory(@Nullable String unitName, @Nullable String requestingBeanName) throws NoSuchBeanDefinitionException { String unitNameForLookup = (unitName != null ? unitName : ""); if (unitNameForLookup.isEmpty()) { unitNameForLookup = this.defaultPersistenceUnitName; } if (!unitNameForLookup.isEmpty()) { return findNamedEntityManagerFactory(unitNameForLookup, requestingBeanName); } else { return findDefaultEntityManagerFactory(requestingBeanName); } } /** * Find an EntityManagerFactory with the given name in the current * Spring application context. * @param unitName the name of the persistence unit (never empty) * @param requestingBeanName the name of the requesting bean * @return the EntityManagerFactory * @throws NoSuchBeanDefinitionException if there is no such EntityManagerFactory in the context */ protected EntityManagerFactory findNamedEntityManagerFactory(String unitName, @Nullable String requestingBeanName) throws NoSuchBeanDefinitionException { Assert.state(this.beanFactory != null, "ListableBeanFactory required for EntityManagerFactory bean lookup"); EntityManagerFactory emf = EntityManagerFactoryUtils.findEntityManagerFactory(this.beanFactory, unitName); if (requestingBeanName != null && this.beanFactory instanceof ConfigurableBeanFactory) { ((ConfigurableBeanFactory) this.beanFactory).registerDependentBean(unitName, requestingBeanName); } return emf; } /** * Find a single default EntityManagerFactory in the Spring application context. * @return the default EntityManagerFactory * @throws NoSuchBeanDefinitionException if there is no single EntityManagerFactory in the context */ protected EntityManagerFactory findDefaultEntityManagerFactory(@Nullable String requestingBeanName) throws NoSuchBeanDefinitionException { Assert.state(this.beanFactory != null, "ListableBeanFactory required for EntityManagerFactory bean lookup"); if (this.beanFactory instanceof ConfigurableListableBeanFactory) { // Fancy variant with dependency registration ConfigurableListableBeanFactory clbf = (ConfigurableListableBeanFactory) this.beanFactory; NamedBeanHolder<EntityManagerFactory> emfHolder = clbf.resolveNamedBean(EntityManagerFactory.class); if (requestingBeanName != null) { clbf.registerDependentBean(emfHolder.getBeanName(), requestingBeanName); } return emfHolder.getBeanInstance(); } else { // Plain variant: just find a default bean return this.beanFactory.getBean(EntityManagerFactory.class); } } /** * Perform a JNDI lookup for the given resource by name. * <p>Called for EntityManagerFactory and EntityManager lookup * when JNDI names are mapped for specific persistence units. * @param jndiName the JNDI name to look up * @param requiredType the required type of the object * @return the obtained object * @throws Exception if the JNDI lookup failed */ protected <T> T lookup(String jndiName, Class<T> requiredType) throws Exception { return new LocatorDelegate().lookup(jndiName, requiredType); } /** * Separate inner class to isolate the JNDI API dependency * (for compatibility with Google App Engine's API white list). */ private class LocatorDelegate { public <T> T lookup(String jndiName, Class<T> requiredType) throws Exception { JndiLocatorDelegate locator = new JndiLocatorDelegate(); if (jndiEnvironment instanceof JndiTemplate) { locator.setJndiTemplate((JndiTemplate) jndiEnvironment); } else if (jndiEnvironment instanceof Properties) { locator.setJndiEnvironment((Properties) jndiEnvironment); } else if (jndiEnvironment != null) { throw new IllegalStateException("Illegal 'jndiEnvironment' type: " + jndiEnvironment.getClass()); } locator.setResourceRef(resourceRef); return locator.lookup(jndiName, requiredType); } } /** * Class representing injection information about an annotated field * or setter method. */ private class PersistenceElement extends InjectionMetadata.InjectedElement { private final String unitName; @Nullable private PersistenceContextType type; private boolean synchronizedWithTransaction = false; @Nullable private Properties properties; public PersistenceElement(Member member, AnnotatedElement ae, @Nullable PropertyDescriptor pd) { super(member, pd); PersistenceContext pc = ae.getAnnotation(PersistenceContext.class); PersistenceUnit pu = ae.getAnnotation(PersistenceUnit.class); Class<?> resourceType = EntityManager.class; if (pc != null) { if (pu != null) { throw new IllegalStateException("Member may only be annotated with either " + "@PersistenceContext or @PersistenceUnit, not both: " + member); } Properties properties = null; PersistenceProperty[] pps = pc.properties(); if (!ObjectUtils.isEmpty(pps)) { properties = new Properties(); for (PersistenceProperty pp : pps) { properties.setProperty(pp.name(), pp.value()); } } this.unitName = pc.unitName(); this.type = pc.type(); this.synchronizedWithTransaction = SynchronizationType.SYNCHRONIZED.equals(pc.synchronization()); this.properties = properties; } else { resourceType = EntityManagerFactory.class; this.unitName = pu.unitName(); } checkResourceType(resourceType); } /** * Resolve the object against the application context. */ @Override protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) { // Resolves to EntityManagerFactory or EntityManager. if (this.type != null) { return (this.type == PersistenceContextType.EXTENDED ? resolveExtendedEntityManager(target, requestingBeanName) : resolveEntityManager(requestingBeanName)); } else { // OK, so we need an EntityManagerFactory... return resolveEntityManagerFactory(requestingBeanName); } } private EntityManagerFactory resolveEntityManagerFactory(@Nullable String requestingBeanName) { // Obtain EntityManagerFactory from JNDI? EntityManagerFactory emf = getPersistenceUnit(this.unitName); if (emf == null) { // Need to search for EntityManagerFactory beans. emf = findEntityManagerFactory(this.unitName, requestingBeanName); } return emf; } private EntityManager resolveEntityManager(@Nullable String requestingBeanName) { // Obtain EntityManager reference from JNDI? EntityManager em = getPersistenceContext(this.unitName, false); if (em == null) { // No pre-built EntityManager found -> build one based on factory. // Obtain EntityManagerFactory from JNDI? EntityManagerFactory emf = getPersistenceUnit(this.unitName); if (emf == null) { // Need to search for EntityManagerFactory beans. emf = findEntityManagerFactory(this.unitName, requestingBeanName); } // Inject a shared transactional EntityManager proxy. if (emf instanceof EntityManagerFactoryInfo && ((EntityManagerFactoryInfo) emf).getEntityManagerInterface() != null) { // Create EntityManager based on the info's vendor-specific type // (which might be more specific than the field's type). em = SharedEntityManagerCreator.createSharedEntityManager(emf, this.properties, this.synchronizedWithTransaction); } else { // Create EntityManager based on the field's type. em = SharedEntityManagerCreator.createSharedEntityManager(emf, this.properties, this.synchronizedWithTransaction, getResourceType()); } } return em; } private EntityManager resolveExtendedEntityManager(Object target, @Nullable String requestingBeanName) { // Obtain EntityManager reference from JNDI? EntityManager em = getPersistenceContext(this.unitName, true); if (em == null) { // No pre-built EntityManager found -> build one based on factory. // Obtain EntityManagerFactory from JNDI? EntityManagerFactory emf = getPersistenceUnit(this.unitName); if (emf == null) { // Need to search for EntityManagerFactory beans. emf = findEntityManagerFactory(this.unitName, requestingBeanName); } // Inject a container-managed extended EntityManager. em = ExtendedEntityManagerCreator.createContainerManagedEntityManager(emf, this.properties, this.synchronizedWithTransaction); } if (em instanceof EntityManagerProxy && beanFactory != null && requestingBeanName != null && beanFactory.containsBean(requestingBeanName) && !beanFactory.isPrototype(requestingBeanName)) { extendedEntityManagersToClose.put(target, ((EntityManagerProxy) em).getTargetEntityManager()); } return em; } } }