Save This Page
Home » spring-framework-2.5.6-with-dependencies » org.springframework » orm » jpa » support » [javadoc | source]
    1   /*
    2    * Copyright 2002-2008 the original author or authors.
    3    *
    4    * Licensed under the Apache License, Version 2.0 (the "License");
    5    * you may not use this file except in compliance with the License.
    6    * You may obtain a copy of the License at
    7    *
    8    *      http://www.apache.org/licenses/LICENSE-2.0
    9    *
   10    * Unless required by applicable law or agreed to in writing, software
   11    * distributed under the License is distributed on an "AS IS" BASIS,
   12    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   13    * See the License for the specific language governing permissions and
   14    * limitations under the License.
   15    */
   16   
   17   package org.springframework.orm.jpa.support;
   18   
   19   import java.beans.PropertyDescriptor;
   20   import java.io.Serializable;
   21   import java.lang.reflect.AnnotatedElement;
   22   import java.lang.reflect.Field;
   23   import java.lang.reflect.Member;
   24   import java.lang.reflect.Method;
   25   import java.lang.reflect.Modifier;
   26   import java.util.Map;
   27   import java.util.Properties;
   28   import java.util.concurrent.ConcurrentHashMap;
   29   
   30   import javax.naming.NamingException;
   31   import javax.persistence.EntityManager;
   32   import javax.persistence.EntityManagerFactory;
   33   import javax.persistence.PersistenceContext;
   34   import javax.persistence.PersistenceContextType;
   35   import javax.persistence.PersistenceProperty;
   36   import javax.persistence.PersistenceUnit;
   37   
   38   import org.springframework.beans.BeanUtils;
   39   import org.springframework.beans.BeansException;
   40   import org.springframework.beans.PropertyValues;
   41   import org.springframework.beans.factory.BeanCreationException;
   42   import org.springframework.beans.factory.BeanFactory;
   43   import org.springframework.beans.factory.BeanFactoryAware;
   44   import org.springframework.beans.factory.BeanFactoryUtils;
   45   import org.springframework.beans.factory.ListableBeanFactory;
   46   import org.springframework.beans.factory.NoSuchBeanDefinitionException;
   47   import org.springframework.beans.factory.annotation.InjectionMetadata;
   48   import org.springframework.beans.factory.config.ConfigurableBeanFactory;
   49   import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
   50   import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
   51   import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
   52   import org.springframework.beans.factory.support.RootBeanDefinition;
   53   import org.springframework.core.Ordered;
   54   import org.springframework.core.PriorityOrdered;
   55   import org.springframework.jndi.JndiLocatorSupport;
   56   import org.springframework.orm.jpa.EntityManagerFactoryInfo;
   57   import org.springframework.orm.jpa.EntityManagerFactoryUtils;
   58   import org.springframework.orm.jpa.EntityManagerProxy;
   59   import org.springframework.orm.jpa.ExtendedEntityManagerCreator;
   60   import org.springframework.orm.jpa.SharedEntityManagerCreator;
   61   import org.springframework.util.ClassUtils;
   62   import org.springframework.util.ObjectUtils;
   63   import org.springframework.util.ReflectionUtils;
   64   
   65   /**
   66    * BeanPostProcessor that processes {@link javax.persistence.PersistenceUnit}
   67    * and {@link javax.persistence.PersistenceContext} annotations, for injection of
   68    * the corresponding JPA resources {@link javax.persistence.EntityManagerFactory}
   69    * and {@link javax.persistence.EntityManager}. Any such annotated fields or methods
   70    * in any Spring-managed object will automatically be injected.
   71    *
   72    * <p>This post-processor will inject sub-interfaces of <code>EntityManagerFactory</code>
   73    * and <code>EntityManager</code> if the annotated fields or methods are declared as such.
   74    * The actual type will be verified early, with the exception of a shared ("transactional")
   75    * <code>EntityManager</code> reference, where type mismatches might be detected as late
   76    * as on the first actual invocation.
   77    *
   78    * <p>Note: In the present implementation, PersistenceAnnotationBeanPostProcessor
   79    * only supports <code>@PersistenceUnit</code> and <code>@PersistenceContext</code>
   80    * with the "unitName" attribute, or no attribute at all (for the default unit).
   81    * If those annotations are present with the "name" attribute at the class level,
   82    * they will simply be ignored, since those only serve as deployment hint
   83    * (as per the Java EE 5 specification).
   84    *
   85    * <p>This post-processor can either obtain EntityManagerFactory beans defined
   86    * in the Spring application context (the default), or obtain EntityManagerFactory
   87    * references from JNDI ("persistence unit references"). In the bean case,
   88    * the persistence unit name will be matched against the actual deployed unit,
   89    * with the bean name used as fallback unit name if no deployed name found.
   90    * Typically, Spring's {@link org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean}
   91    * will be used for setting up such EntityManagerFactory beans. Alternatively,
   92    * such beans may also be obtained from JNDI, e.g. using the <code>jee:jndi-lookup</code>
   93    * XML configuration element (with the bean name matching the requested unit name).
   94    * In both cases, the post-processor definition will look as simple as this:
   95    *
   96    * <pre class="code">
   97    * &lt;bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/&gt;</pre>
   98    *
   99    * In the JNDI case, specify the corresponding JNDI names in this post-processor's
  100    * {@link #setPersistenceUnits "persistenceUnits" map}, typically with matching
  101    * <code>persistence-unit-ref</code> entries in the Java EE deployment descriptor.
  102    * By default, those names are considered as resource references (according to the
  103    * Java EE resource-ref convention), located underneath the "java:comp/env/" namespace.
  104    * For example:
  105    *
  106    * <pre class="code">
  107    * &lt;bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"&gt;
  108    *   &lt;property name="persistenceUnits"&gt;
  109    *     &lt;map/gt;
  110    *       &lt;entry key="unit1" value="persistence/unit1"/&gt;
  111    *       &lt;entry key="unit2" value="persistence/unit2"/&gt;
  112    *     &lt;/map/gt;
  113    *   &lt;/property&gt;
  114    * &lt;/bean&gt;</pre>
  115    *
  116    * In this case, the specified persistence units will always be resolved in JNDI
  117    * rather than as Spring-defined beans. The entire persistence unit deployment,
  118    * including the weaving of persistent classes, is then up to the Java EE server.
  119    * Persistence contexts (i.e. EntityManager references) will be built based on
  120    * those server-provided EntityManagerFactory references, using Spring's own
  121    * transaction synchronization facilities for transactional EntityManager handling
  122    * (typically with Spring's <code>@Transactional</code> annotation for demarcation
  123    * and {@link org.springframework.transaction.jta.JtaTransactionManager} as backend).
  124    *
  125    * <p>If you prefer the Java EE server's own EntityManager handling, specify entries
  126    * in this post-processor's {@link #setPersistenceContexts "persistenceContexts" map}
  127    * (or {@link #setExtendedPersistenceContexts "extendedPersistenceContexts" map},
  128    * typically with matching <code>persistence-context-ref</code> entries in the
  129    * Java EE deployment descriptor. For example:
  130    *
  131    * <pre class="code">
  132    * &lt;bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"&gt;
  133    *   &lt;property name="persistenceContexts"&gt;
  134    *     &lt;map/gt;
  135    *       &lt;entry key="unit1" value="persistence/context1"/&gt;
  136    *       &lt;entry key="unit2" value="persistence/context2"/&gt;
  137    *     &lt;/map/gt;
  138    *   &lt;/property&gt;
  139    * &lt;/bean&gt;</pre>
  140    *
  141    * If the application only obtains EntityManager references in the first place,
  142    * this is all you need to specify. If you need EntityManagerFactory references
  143    * as well, specify entries for both "persistenceUnits" and "persistenceContexts",
  144    * pointing to matching JNDI locations.
  145    *
  146    * <p><b>NOTE: In general, do not inject EXTENDED EntityManagers into STATELESS beans,
  147    * i.e. do not use <code>@PersistenceContext</code> with type <code>EXTENDED</code> in
  148    * Spring beans defined with scope 'singleton' (Spring's default scope).</b>
  149    * Extended EntityManagers are <i>not</i> thread-safe, hence they must not be used
  150    * in concurrently accessed beans (which Spring-managed singletons usually are).
  151    *
  152    * <p>Note: A default PersistenceAnnotationBeanPostProcessor will be registered
  153    * by the "context:annotation-config" and "context:component-scan" XML tags.
  154    * Remove or turn off the default annotation configuration there if you intend
  155    * to specify a custom PersistenceAnnotationBeanPostProcessor bean definition.
  156    *
  157    * @author Rod Johnson
  158    * @author Juergen Hoeller
  159    * @since 2.0
  160    * @see javax.persistence.PersistenceUnit
  161    * @see javax.persistence.PersistenceContext
  162    */
  163   public class PersistenceAnnotationBeanPostProcessor extends JndiLocatorSupport
  164   		implements InstantiationAwareBeanPostProcessor, DestructionAwareBeanPostProcessor,
  165   		MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware, Serializable {
  166   
  167   	private transient Map<String, String> persistenceUnits;
  168   
  169   	private transient Map<String, String> persistenceContexts;
  170   
  171   	private transient Map<String, String> extendedPersistenceContexts;
  172   
  173   	private transient String defaultPersistenceUnitName = "";
  174   
  175   	private int order = Ordered.LOWEST_PRECEDENCE - 4;
  176   
  177   	private transient ListableBeanFactory beanFactory;
  178   
  179   	private transient final Map<Class<?>, InjectionMetadata> injectionMetadataCache =
  180   			new ConcurrentHashMap<Class<?>, InjectionMetadata>();
  181   
  182   	private final Map<Object, EntityManager> extendedEntityManagersToClose =
  183   			new ConcurrentHashMap<Object, EntityManager>();
  184   
  185   
  186   	public PersistenceAnnotationBeanPostProcessor() {
  187   		setResourceRef(true);
  188   	}
  189   
  190   
  191   	/**
  192   	 * Specify the persistence units for EntityManagerFactory lookups,
  193   	 * as a Map from persistence unit name to persistence unit JNDI name
  194   	 * (which needs to resolve to an EntityManagerFactory instance).
  195   	 * <p>JNDI names specified here should refer to <code>persistence-unit-ref</code>
  196   	 * entries in the Java EE deployment descriptor, matching the target persistence unit.
  197   	 * <p>In case of no unit name specified in the annotation, the specified value
  198   	 * for the {@link #setDefaultPersistenceUnitName default persistence unit}
  199   	 * will be taken (by default, the value mapped to the empty String),
  200   	 * or simply the single persistence unit if there is only one.
  201   	 * <p>This is mainly intended for use in a Java EE 5 environment, with all
  202   	 * lookup driven by the standard JPA annotations, and all EntityManagerFactory
  203   	 * references obtained from JNDI. No separate EntityManagerFactory bean
  204   	 * definitions are necessary in such a scenario.
  205   	 * <p>If no corresponding "persistenceContexts"/"extendedPersistenceContexts"
  206   	 * are specified, <code>@PersistenceContext</code> will be resolved to
  207   	 * EntityManagers built on top of the EntityManagerFactory defined here.
  208   	 * Note that those will be Spring-managed EntityManagers, which implement
  209   	 * transaction synchronization based on Spring's facilities.
  210   	 * If you prefer the Java EE 5 server's own EntityManager handling,
  211   	 * specify corresponding "persistenceContexts"/"extendedPersistenceContexts".
  212   	 */
  213   	public void setPersistenceUnits(Map<String, String> persistenceUnits) {
  214   		this.persistenceUnits = persistenceUnits;
  215   	}
  216   
  217   	/**
  218   	 * Specify the <i>transactional</i> persistence contexts for EntityManager lookups,
  219   	 * as a Map from persistence unit name to persistence context JNDI name
  220   	 * (which needs to resolve to an EntityManager instance).
  221   	 * <p>JNDI names specified here should refer to <code>persistence-context-ref</code>
  222   	 * entries in the Java EE deployment descriptors, matching the target persistence unit
  223   	 * and being set up with persistence context type <code>Transaction</code>.
  224   	 * <p>In case of no unit name specified in the annotation, the specified value
  225   	 * for the {@link #setDefaultPersistenceUnitName default persistence unit}
  226   	 * will be taken (by default, the value mapped to the empty String),
  227   	 * or simply the single persistence unit if there is only one.
  228   	 * <p>This is mainly intended for use in a Java EE 5 environment, with all
  229   	 * lookup driven by the standard JPA annotations, and all EntityManager
  230   	 * references obtained from JNDI. No separate EntityManagerFactory bean
  231   	 * definitions are necessary in such a scenario, and all EntityManager
  232   	 * handling is done by the Java EE 5 server itself.
  233   	 */
  234   	public void setPersistenceContexts(Map<String, String> persistenceContexts) {
  235   		this.persistenceContexts = persistenceContexts;
  236   	}
  237   
  238   	/**
  239   	 * Specify the <i>extended</i> persistence contexts for EntityManager lookups,
  240   	 * as a Map from persistence unit name to persistence context JNDI name
  241   	 * (which needs to resolve to an EntityManager instance).
  242   	 * <p>JNDI names specified here should refer to <code>persistence-context-ref</code>
  243   	 * entries in the Java EE deployment descriptors, matching the target persistence unit
  244   	 * and being set up with persistence context type <code>Extended</code>.
  245   	 * <p>In case of no unit name specified in the annotation, the specified value
  246   	 * for the {@link #setDefaultPersistenceUnitName default persistence unit}
  247   	 * will be taken (by default, the value mapped to the empty String),
  248   	 * or simply the single persistence unit if there is only one.
  249   	 * <p>This is mainly intended for use in a Java EE 5 environment, with all
  250   	 * lookup driven by the standard JPA annotations, and all EntityManager
  251   	 * references obtained from JNDI. No separate EntityManagerFactory bean
  252   	 * definitions are necessary in such a scenario, and all EntityManager
  253   	 * handling is done by the Java EE 5 server itself.
  254   	 */
  255   	public void setExtendedPersistenceContexts(Map<String, String> extendedPersistenceContexts) {
  256   		this.extendedPersistenceContexts = extendedPersistenceContexts;
  257   	}
  258   
  259   	/**
  260   	 * Specify the default persistence unit name, to be used in case
  261   	 * of no unit name specified in an <code>@PersistenceUnit</code> /
  262   	 * <code>@PersistenceContext</code> annotation.
  263   	 * <p>This is mainly intended for lookups in the application context,
  264   	 * indicating the target persistence unit name (typically matching
  265   	 * the bean name), but also applies to lookups in the
  266   	 * {@link #setPersistenceUnits "persistenceUnits"} /
  267   	 * {@link #setPersistenceContexts "persistenceContexts"} /
  268   	 * {@link #setExtendedPersistenceContexts "extendedPersistenceContexts"} map,
  269   	 * avoiding the need for duplicated mappings for the empty String there.
  270   	 * <p>Default is to check for a single EntityManagerFactory bean
  271   	 * in the Spring application context, if any. If there are multiple
  272   	 * such factories, either specify this default persistence unit name
  273   	 * or explicitly refer to named persistence units in your annotations.
  274   	 */
  275   	public void setDefaultPersistenceUnitName(String unitName) {
  276   		this.defaultPersistenceUnitName = (unitName != null ? unitName : "");
  277   	}
  278   
  279   	public void setOrder(int order) {
  280   	  this.order = order;
  281   	}
  282   
  283   	public int getOrder() {
  284   	  return this.order;
  285   	}
  286   
  287   	public void setBeanFactory(BeanFactory beanFactory) {
  288   		if (beanFactory instanceof ListableBeanFactory) {
  289   			this.beanFactory = (ListableBeanFactory) beanFactory;
  290   		}
  291   	}
  292   
  293   
  294   	public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class beanType, String beanName) {
  295   		if (beanType != null) {
  296   			InjectionMetadata metadata = findPersistenceMetadata(beanType);
  297   			metadata.checkConfigMembers(beanDefinition);
  298   		}
  299   	}
  300   
  301   	public Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException {
  302   		return null;
  303   	}
  304   
  305   	public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
  306   		InjectionMetadata metadata = findPersistenceMetadata(bean.getClass());
  307   		try {
  308   			metadata.injectFields(bean, beanName);
  309   		}
  310   		catch (Throwable ex) {
  311   			throw new BeanCreationException(beanName, "Injection of persistence fields failed", ex);
  312   		}
  313   		return true;
  314   	}
  315   
  316   	public PropertyValues postProcessPropertyValues(
  317   			PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
  318   
  319   		InjectionMetadata metadata = findPersistenceMetadata(bean.getClass());
  320   		try {
  321   			metadata.injectMethods(bean, beanName, pvs);
  322   		}
  323   		catch (Throwable ex) {
  324   			throw new BeanCreationException(beanName, "Injection of persistence methods failed", ex);
  325   		}
  326   		return pvs;
  327   	}
  328   
  329   	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
  330   		return bean;
  331   	}
  332   
  333   	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
  334   		return bean;
  335   	}
  336   
  337   	public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
  338   		EntityManager emToClose = this.extendedEntityManagersToClose.remove(bean);
  339   		EntityManagerFactoryUtils.closeEntityManager(emToClose);
  340   	}
  341   
  342   
  343   	private InjectionMetadata findPersistenceMetadata(final Class clazz) {
  344   		// Quick check on the concurrent map first, with minimal locking.
  345   		InjectionMetadata metadata = this.injectionMetadataCache.get(clazz);
  346   		if (metadata == null) {
  347   			synchronized (this.injectionMetadataCache) {
  348   				metadata = this.injectionMetadataCache.get(clazz);
  349   				if (metadata == null) {
  350   					final InjectionMetadata newMetadata = new InjectionMetadata(clazz);
  351   					ReflectionUtils.doWithFields(clazz, new ReflectionUtils.FieldCallback() {
  352   						public void doWith(Field field) {
  353   							PersistenceContext pc = field.getAnnotation(PersistenceContext.class);
  354   							PersistenceUnit pu = field.getAnnotation(PersistenceUnit.class);
  355   							if (pc != null || pu != null) {
  356   								if (Modifier.isStatic(field.getModifiers())) {
  357   									throw new IllegalStateException("Persistence annotations are not supported on static fields");
  358   								}
  359   								newMetadata.addInjectedField(new PersistenceElement(field, null));
  360   							}
  361   						}
  362   					});
  363   					ReflectionUtils.doWithMethods(clazz, new ReflectionUtils.MethodCallback() {
  364   						public void doWith(Method method) {
  365   							PersistenceContext pc = method.getAnnotation(PersistenceContext.class);
  366   							PersistenceUnit pu = method.getAnnotation(PersistenceUnit.class);
  367   							if (pc != null || pu != null &&
  368   									method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
  369   								if (Modifier.isStatic(method.getModifiers())) {
  370   									throw new IllegalStateException("Persistence annotations are not supported on static methods");
  371   								}
  372   								if (method.getParameterTypes().length != 1) {
  373   									throw new IllegalStateException("Persistence annotation requires a single-arg method: " + method);
  374   								}
  375   								PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method);
  376   								newMetadata.addInjectedMethod(new PersistenceElement(method, pd));
  377   							}
  378   						}
  379   					});
  380   					metadata = newMetadata;
  381   					this.injectionMetadataCache.put(clazz, metadata);
  382   				}
  383   			}
  384   		}
  385   		return metadata;
  386   	}
  387   
  388   	/**
  389   	 * Return a specified persistence unit for the given unit name,
  390   	 * as defined through the "persistenceUnits" map.
  391   	 * @param unitName the name of the persistence unit
  392   	 * @return the corresponding EntityManagerFactory,
  393   	 * or <code>null</code> if none found
  394   	 * @see #setPersistenceUnits
  395   	 */
  396   	protected EntityManagerFactory getPersistenceUnit(String unitName) {
  397   		if (this.persistenceUnits != null) {
  398   			String unitNameForLookup = (unitName != null ? unitName : "");
  399   			if ("".equals(unitNameForLookup)) {
  400   				unitNameForLookup = this.defaultPersistenceUnitName;
  401   			}
  402   			String jndiName = this.persistenceUnits.get(unitNameForLookup);
  403   			if (jndiName == null && "".equals(unitNameForLookup) && this.persistenceUnits.size() == 1) {
  404   				jndiName = this.persistenceUnits.values().iterator().next();
  405   			}
  406   			if (jndiName != null) {
  407   				try {
  408   					return (EntityManagerFactory) lookup(jndiName, EntityManagerFactory.class);
  409   				}
  410   				catch (NamingException ex) {
  411   					throw new IllegalStateException("Could not obtain EntityManagerFactory [" + jndiName + "] from JNDI", ex);
  412   				}
  413   			}
  414   		}
  415   		return null;
  416   	}
  417   
  418   	/**
  419   	 * Return a specified persistence context for the given unit name, as defined
  420   	 * through the "persistenceContexts" (or "extendedPersistenceContexts") map.
  421   	 * @param unitName the name of the persistence unit
  422   	 * @param extended whether to obtain an extended persistence context
  423   	 * @return the corresponding EntityManager, or <code>null</code> if none found
  424   	 * @see #setPersistenceContexts
  425   	 * @see #setExtendedPersistenceContexts
  426   	 */
  427   	protected EntityManager getPersistenceContext(String unitName, boolean extended) {
  428   		Map<String, String> contexts = (extended ? this.extendedPersistenceContexts : this.persistenceContexts);
  429   		if (contexts != null) {
  430   			String unitNameForLookup = (unitName != null ? unitName : "");
  431   			if ("".equals(unitNameForLookup)) {
  432   				unitNameForLookup = this.defaultPersistenceUnitName;
  433   			}
  434   			String jndiName = contexts.get(unitNameForLookup);
  435   			if (jndiName == null && "".equals(unitNameForLookup) && contexts.size() == 1) {
  436   				jndiName = contexts.values().iterator().next();
  437   			}
  438   			if (jndiName != null) {
  439   				try {
  440   					return (EntityManager) lookup(jndiName, EntityManager.class);
  441   				}
  442   				catch (NamingException ex) {
  443   					throw new IllegalStateException("Could not obtain EntityManager [" + jndiName + "] from JNDI", ex);
  444   				}
  445   			}
  446   		}
  447   		return null;
  448   	}
  449   
  450   	/**
  451   	 * Find an EntityManagerFactory with the given name in the current Spring
  452   	 * application context, falling back to a single default EntityManagerFactory
  453   	 * (if any) in case of no unit name specified.
  454   	 * @param unitName the name of the persistence unit (may be <code>null</code> or empty)
  455   	 * @param requestingBeanName the name of the requesting bean
  456   	 * @return the EntityManagerFactory
  457   	 * @throws NoSuchBeanDefinitionException if there is no such EntityManagerFactory in the context
  458   	 */
  459   	protected EntityManagerFactory findEntityManagerFactory(String unitName, String requestingBeanName)
  460   			throws NoSuchBeanDefinitionException {
  461   
  462   		if (this.beanFactory == null) {
  463   			throw new IllegalStateException("ListableBeanFactory required for EntityManagerFactory bean lookup");
  464   		}
  465   		String unitNameForLookup = (unitName != null ? unitName : "");
  466   		if ("".equals(unitNameForLookup)) {
  467   			unitNameForLookup = this.defaultPersistenceUnitName;
  468   		}
  469   		if (!"".equals(unitNameForLookup)) {
  470   			return findNamedEntityManagerFactory(unitNameForLookup, requestingBeanName);
  471   		}
  472   		else {
  473   			return findDefaultEntityManagerFactory(requestingBeanName);
  474   		}
  475   	}
  476   
  477   	/**
  478   	 * Find an EntityManagerFactory with the given name in the current
  479   	 * Spring application context.
  480   	 * @param unitName the name of the persistence unit (never empty)
  481   	 * @param requestingBeanName the name of the requesting bean
  482   	 * @return the EntityManagerFactory
  483   	 * @throws NoSuchBeanDefinitionException if there is no such EntityManagerFactory in the context
  484   	 */
  485   	protected EntityManagerFactory findNamedEntityManagerFactory(String unitName, String requestingBeanName)
  486   			throws NoSuchBeanDefinitionException {
  487   
  488   		EntityManagerFactory emf = EntityManagerFactoryUtils.findEntityManagerFactory(this.beanFactory, unitName);
  489   		if (this.beanFactory instanceof ConfigurableBeanFactory) {
  490   			((ConfigurableBeanFactory) this.beanFactory).registerDependentBean(unitName, requestingBeanName);
  491   		}
  492   		return emf;
  493   	}
  494   
  495   	/**
  496   	 * Find a single default EntityManagerFactory in the Spring application context.
  497   	 * @return the default EntityManagerFactory
  498   	 * @throws NoSuchBeanDefinitionException if there is no single EntityManagerFactory in the context
  499   	 */
  500   	protected EntityManagerFactory findDefaultEntityManagerFactory(String requestingBeanName)
  501   			throws NoSuchBeanDefinitionException{
  502   
  503   		String[] beanNames =
  504   				BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, EntityManagerFactory.class);
  505   		if (beanNames.length == 1) {
  506   			String unitName = beanNames[0];
  507   			EntityManagerFactory emf = (EntityManagerFactory) this.beanFactory.getBean(unitName);
  508   			if (this.beanFactory instanceof ConfigurableBeanFactory) {
  509   				((ConfigurableBeanFactory) this.beanFactory).registerDependentBean(unitName, requestingBeanName);
  510   			}
  511   			return emf;
  512   		}
  513   		else {
  514   			throw new NoSuchBeanDefinitionException(
  515   					EntityManagerFactory.class, "expected single bean but found " + beanNames.length);
  516   		}
  517   	}
  518   
  519   
  520   	/**
  521   	 * Class representing injection information about an annotated field
  522   	 * or setter method.
  523   	 */
  524   	private class PersistenceElement extends InjectionMetadata.InjectedElement {
  525   
  526   		private final String unitName;
  527   
  528   		private PersistenceContextType type;
  529   
  530   		private Properties properties;
  531   
  532   		public PersistenceElement(Member member, PropertyDescriptor pd) {
  533   			super(member, pd);
  534   			AnnotatedElement ae = (AnnotatedElement) member;
  535   			PersistenceContext pc = ae.getAnnotation(PersistenceContext.class);
  536   			PersistenceUnit pu = ae.getAnnotation(PersistenceUnit.class);
  537   			Class resourceType = EntityManager.class;
  538   			if (pc != null) {
  539   				if (pu != null) {
  540   					throw new IllegalStateException("Member may only be annotated with either " +
  541   							"@PersistenceContext or @PersistenceUnit, not both: " + member);
  542   				}
  543   				Properties properties = null;
  544   				PersistenceProperty[] pps = pc.properties();
  545   				if (!ObjectUtils.isEmpty(pps)) {
  546   					properties = new Properties();
  547   					for (int i = 0; i < pps.length; i++) {
  548   						PersistenceProperty pp = pps[i];
  549   						properties.setProperty(pp.name(), pp.value());
  550   					}
  551   				}
  552   				this.unitName = pc.unitName();
  553   				this.type = pc.type();
  554   				this.properties = properties;
  555   			}
  556   			else {
  557   				resourceType = EntityManagerFactory.class;
  558   				this.unitName = pu.unitName();
  559   			}
  560   			checkResourceType(resourceType);
  561   		}
  562   
  563   		/**
  564   		 * Resolve the object against the application context.
  565   		 */
  566   		@Override
  567   		protected Object getResourceToInject(Object target, String requestingBeanName) {
  568   			// Resolves to EntityManagerFactory or EntityManager.
  569   			if (this.type != null) {
  570   				return (this.type == PersistenceContextType.EXTENDED ?
  571   						resolveExtendedEntityManager(target, requestingBeanName) :
  572   						resolveEntityManager(requestingBeanName));
  573   			}
  574   			else {
  575   				// OK, so we need an EntityManagerFactory...
  576   				return resolveEntityManagerFactory(requestingBeanName);
  577   			}
  578   		}
  579   
  580   		private EntityManagerFactory resolveEntityManagerFactory(String requestingBeanName) {
  581   			// Obtain EntityManagerFactory from JNDI?
  582   			EntityManagerFactory emf = getPersistenceUnit(this.unitName);
  583   			if (emf == null) {
  584   				// Need to search for EntityManagerFactory beans.
  585   				emf = findEntityManagerFactory(this.unitName, requestingBeanName);
  586   			}
  587   			return emf;
  588   		}
  589   
  590   		private EntityManager resolveEntityManager(String requestingBeanName) {
  591   			// Obtain EntityManager reference from JNDI?
  592   			EntityManager em = getPersistenceContext(this.unitName, false);
  593   			if (em == null) {
  594   				// No pre-built EntityManager found -> build one based on factory.
  595   				// Obtain EntityManagerFactory from JNDI?
  596   				EntityManagerFactory emf = getPersistenceUnit(this.unitName);
  597   				if (emf == null) {
  598   					// Need to search for EntityManagerFactory beans.
  599   					emf = findEntityManagerFactory(this.unitName, requestingBeanName);
  600   				}
  601   				// Inject a shared transactional EntityManager proxy.
  602   				if (emf instanceof EntityManagerFactoryInfo &&
  603   						((EntityManagerFactoryInfo) emf).getEntityManagerInterface() != null) {
  604   					// Create EntityManager based on the info's vendor-specific type
  605   					// (which might be more specific than the field's type).
  606   					em = SharedEntityManagerCreator.createSharedEntityManager(emf, this.properties);
  607   				}
  608   				else {
  609   					// Create EntityManager based on the field's type.
  610   					em = SharedEntityManagerCreator.createSharedEntityManager(emf, this.properties, getResourceType());
  611   				}
  612   			}
  613   			return em;
  614   		}
  615   
  616   		private EntityManager resolveExtendedEntityManager(Object target, String requestingBeanName) {
  617   			// Obtain EntityManager reference from JNDI?
  618   			EntityManager em = getPersistenceContext(this.unitName, true);
  619   			if (em == null) {
  620   				// No pre-built EntityManager found -> build one based on factory.
  621   				// Obtain EntityManagerFactory from JNDI?
  622   				EntityManagerFactory emf = getPersistenceUnit(this.unitName);
  623   				if (emf == null) {
  624   					// Need to search for EntityManagerFactory beans.
  625   					emf = findEntityManagerFactory(this.unitName, requestingBeanName);
  626   				}
  627   				// Inject a container-managed extended EntityManager.
  628   				em = ExtendedEntityManagerCreator.createContainerManagedEntityManager(emf, this.properties);
  629   			}
  630   			if (em instanceof EntityManagerProxy &&
  631   					beanFactory != null && !beanFactory.isPrototype(requestingBeanName)) {
  632   				extendedEntityManagersToClose.put(target, ((EntityManagerProxy) em).getTargetEntityManager());
  633   			}
  634   			return em;
  635   		}
  636   	}
  637   
  638   }

Save This Page
Home » spring-framework-2.5.6-with-dependencies » org.springframework » orm » jpa » support » [javadoc | source]