Save This Page
Home » spring-framework-2.5.6-with-dependencies » org.springframework » orm » jpa » [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;
   18   
   19   import java.util.Map;
   20   
   21   import javax.persistence.EntityExistsException;
   22   import javax.persistence.EntityManager;
   23   import javax.persistence.EntityManagerFactory;
   24   import javax.persistence.EntityNotFoundException;
   25   import javax.persistence.NoResultException;
   26   import javax.persistence.NonUniqueResultException;
   27   import javax.persistence.OptimisticLockException;
   28   import javax.persistence.PersistenceException;
   29   import javax.persistence.TransactionRequiredException;
   30   
   31   import org.apache.commons.logging.Log;
   32   import org.apache.commons.logging.LogFactory;
   33   
   34   import org.springframework.beans.factory.BeanFactoryUtils;
   35   import org.springframework.beans.factory.ListableBeanFactory;
   36   import org.springframework.beans.factory.NoSuchBeanDefinitionException;
   37   import org.springframework.core.Ordered;
   38   import org.springframework.dao.DataAccessException;
   39   import org.springframework.dao.DataAccessResourceFailureException;
   40   import org.springframework.dao.DataIntegrityViolationException;
   41   import org.springframework.dao.EmptyResultDataAccessException;
   42   import org.springframework.dao.IncorrectResultSizeDataAccessException;
   43   import org.springframework.dao.InvalidDataAccessApiUsageException;
   44   import org.springframework.jdbc.datasource.DataSourceUtils;
   45   import org.springframework.transaction.support.ResourceHolder;
   46   import org.springframework.transaction.support.ResourceHolderSynchronization;
   47   import org.springframework.transaction.support.TransactionSynchronizationManager;
   48   import org.springframework.util.Assert;
   49   import org.springframework.util.CollectionUtils;
   50   
   51   /**
   52    * Helper class featuring methods for JPA EntityManager handling,
   53    * allowing for reuse of EntityManager instances within transactions.
   54    * Also provides support for exception translation.
   55    *
   56    * <p>Mainly intended for internal use within the framework.
   57    *
   58    * @author Juergen Hoeller
   59    * @since 2.0
   60    */
   61   public abstract class EntityManagerFactoryUtils {
   62   
   63   	/**
   64   	 * Order value for TransactionSynchronization objects that clean up JPA
   65   	 * EntityManagers. Return DataSourceUtils.CONNECTION_SYNCHRONIZATION_ORDER - 100
   66   	 * to execute EntityManager cleanup before JDBC Connection cleanup, if any.
   67   	 * @see org.springframework.jdbc.datasource.DataSourceUtils#CONNECTION_SYNCHRONIZATION_ORDER
   68   	 */
   69   	public static final int ENTITY_MANAGER_SYNCHRONIZATION_ORDER =
   70   			DataSourceUtils.CONNECTION_SYNCHRONIZATION_ORDER - 100;
   71   
   72   	private static final Log logger = LogFactory.getLog(EntityManagerFactoryUtils.class);
   73   
   74   
   75   	/**
   76   	 * Find an EntityManagerFactory with the given name in the given
   77   	 * Spring application context (represented as ListableBeanFactory).
   78   	 * <p>The specified unit name will be matched against the configured
   79   	 * peristence unit, provided that a discovered EntityManagerFactory
   80   	 * implements the {@link EntityManagerFactoryInfo} interface. If not,
   81   	 * the persistence unit name will be matched against the Spring bean name,
   82   	 * assuming that the EntityManagerFactory bean names follow that convention.
   83   	 * @param beanFactory the ListableBeanFactory to search
   84   	 * @param unitName the name of the persistence unit (never empty)
   85   	 * @return the EntityManagerFactory
   86   	 * @throws NoSuchBeanDefinitionException if there is no such EntityManagerFactory in the context
   87   	 * @see EntityManagerFactoryInfo#getPersistenceUnitName()
   88   	 */
   89   	public static EntityManagerFactory findEntityManagerFactory(
   90   			ListableBeanFactory beanFactory, String unitName) throws NoSuchBeanDefinitionException {
   91   
   92   		Assert.notNull(beanFactory, "ListableBeanFactory must not be null");
   93   		Assert.hasLength(unitName, "Unit name must not be empty");
   94   
   95   		// See whether we can find an EntityManagerFactory with matching persistence unit name.
   96   		String[] candidateNames =
   97   				BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, EntityManagerFactory.class);
   98   		for (String candidateName : candidateNames) {
   99   			EntityManagerFactory emf = (EntityManagerFactory) beanFactory.getBean(candidateName);
  100   			if (emf instanceof EntityManagerFactoryInfo) {
  101   				if (unitName.equals(((EntityManagerFactoryInfo) emf).getPersistenceUnitName())) {
  102   					return emf;
  103   				}
  104   			}
  105   		}
  106   		// No matching persistence unit found - simply take the EntityManagerFactory
  107   		// with the persistence unit name as bean name (by convention).
  108   		return (EntityManagerFactory) beanFactory.getBean(unitName, EntityManagerFactory.class);
  109   	}
  110   
  111   	/**
  112   	 * Obtain a JPA EntityManager from the given factory. Is aware of a
  113   	 * corresponding EntityManager bound to the current thread,
  114   	 * for example when using JpaTransactionManager.
  115   	 * <p>Note: Will return <code>null</code> if no thread-bound EntityManager found!
  116   	 * @param emf EntityManagerFactory to create the EntityManager with
  117   	 * @return the EntityManager, or <code>null</code> if none found
  118   	 * @throws DataAccessResourceFailureException if the EntityManager couldn't be obtained
  119   	 * @see JpaTransactionManager
  120   	 */
  121   	public static EntityManager getTransactionalEntityManager(EntityManagerFactory emf)
  122   			throws DataAccessResourceFailureException {
  123   
  124   		return getTransactionalEntityManager(emf, null);
  125   	}
  126   
  127   	/**
  128   	 * Obtain a JPA EntityManager from the given factory. Is aware of a
  129   	 * corresponding EntityManager bound to the current thread,
  130   	 * for example when using JpaTransactionManager.
  131   	 * <p>Note: Will return <code>null</code> if no thread-bound EntityManager found!
  132   	 * @param emf EntityManagerFactory to create the EntityManager with
  133   	 * @param properties the properties to be passed into the <code>createEntityManager</code>
  134   	 * call (may be <code>null</code>)
  135   	 * @return the EntityManager, or <code>null</code> if none found
  136   	 * @throws DataAccessResourceFailureException if the EntityManager couldn't be obtained
  137   	 * @see JpaTransactionManager
  138   	 */
  139   	public static EntityManager getTransactionalEntityManager(EntityManagerFactory emf, Map properties)
  140   			throws DataAccessResourceFailureException {
  141   		try {
  142   			return doGetTransactionalEntityManager(emf, properties);
  143   		}
  144   		catch (PersistenceException ex) {
  145   			throw new DataAccessResourceFailureException("Could not obtain JPA EntityManager", ex);
  146   		}
  147   	}
  148   
  149   	/**
  150   	 * Obtain a JPA EntityManager from the given factory. Is aware of a
  151   	 * corresponding EntityManager bound to the current thread,
  152   	 * for example when using JpaTransactionManager.
  153   	 * <p>Same as <code>getEntityManager</code>, but throwing the original PersistenceException.
  154   	 * @param emf EntityManagerFactory to create the EntityManager with
  155   	 * @param properties the properties to be passed into the <code>createEntityManager</code>
  156   	 * call (may be <code>null</code>)
  157   	 * @return the EntityManager, or <code>null</code> if none found
  158   	 * @throws javax.persistence.PersistenceException if the EntityManager couldn't be created
  159   	 * @see #getTransactionalEntityManager(javax.persistence.EntityManagerFactory)
  160   	 * @see JpaTransactionManager
  161   	 */
  162   	public static EntityManager doGetTransactionalEntityManager(
  163   			EntityManagerFactory emf, Map properties) throws PersistenceException {
  164   
  165   		Assert.notNull(emf, "No EntityManagerFactory specified");
  166   
  167   		EntityManagerHolder emHolder =
  168   				(EntityManagerHolder) TransactionSynchronizationManager.getResource(emf);
  169   		if (emHolder != null) {
  170   			if (!emHolder.isSynchronizedWithTransaction() &&
  171   					TransactionSynchronizationManager.isSynchronizationActive()) {
  172   				// Try to explicitly synchronize the EntityManager itself
  173   				// with an ongoing JTA transaction, if any.
  174   				try {
  175   					emHolder.getEntityManager().joinTransaction();
  176   				}
  177   				catch (TransactionRequiredException ex) {
  178   					logger.debug("Could not join JTA transaction because none was active", ex);
  179   				}
  180   				Object transactionData = prepareTransaction(emHolder.getEntityManager(), emf);
  181   				TransactionSynchronizationManager.registerSynchronization(
  182   						new EntityManagerSynchronization(emHolder, emf, transactionData, false));
  183   				emHolder.setSynchronizedWithTransaction(true);
  184   			}
  185   			return emHolder.getEntityManager();
  186   		}
  187   
  188   		if (!TransactionSynchronizationManager.isSynchronizationActive()) {
  189   			// Indicate that we can't obtain a transactional EntityManager.
  190   			return null;
  191   		}
  192   
  193   		// Create a new EntityManager for use within the current transaction.
  194   		logger.debug("Opening JPA EntityManager");
  195   		EntityManager em =
  196   				(!CollectionUtils.isEmpty(properties) ? emf.createEntityManager(properties) : emf.createEntityManager());
  197   
  198   		if (TransactionSynchronizationManager.isSynchronizationActive()) {
  199   			logger.debug("Registering transaction synchronization for JPA EntityManager");
  200   			// Use same EntityManager for further JPA actions within the transaction.
  201   			// Thread object will get removed by synchronization at transaction completion.
  202   			emHolder = new EntityManagerHolder(em);
  203   			Object transactionData = prepareTransaction(em, emf);
  204   			TransactionSynchronizationManager.registerSynchronization(
  205   					new EntityManagerSynchronization(emHolder, emf, transactionData, true));
  206   			emHolder.setSynchronizedWithTransaction(true);
  207   			TransactionSynchronizationManager.bindResource(emf, emHolder);
  208   		}
  209   
  210   		return em;
  211   	}
  212   
  213   	/**
  214   	 * Prepare a transaction on the given EntityManager, if possible.
  215   	 * @param em the EntityManager to prepare
  216   	 * @param emf the EntityManagerFactory that the EntityManager has been created with
  217   	 * @return an arbitrary object that holds transaction data, if any
  218   	 * (to be passed into cleanupTransaction)
  219   	 * @see JpaDialect#prepareTransaction
  220   	 */
  221   	private static Object prepareTransaction(EntityManager em, EntityManagerFactory emf) {
  222   		if (emf instanceof EntityManagerFactoryInfo) {
  223   			EntityManagerFactoryInfo emfInfo = (EntityManagerFactoryInfo) emf;
  224   			JpaDialect jpaDialect = emfInfo.getJpaDialect();
  225   			if (jpaDialect != null) {
  226   				return jpaDialect.prepareTransaction(em,
  227   						TransactionSynchronizationManager.isCurrentTransactionReadOnly(),
  228   						TransactionSynchronizationManager.getCurrentTransactionName());
  229   			}
  230   		}
  231   		return null;
  232   	}
  233   
  234   	/**
  235   	 * Prepare a transaction on the given EntityManager, if possible.
  236   	 * @param transactionData arbitrary object that holds transaction data, if any
  237   	 * (as returned by prepareTransaction)
  238   	 * @param emf the EntityManagerFactory that the EntityManager has been created with
  239   	 * @see JpaDialect#cleanupTransaction
  240   	 */
  241   	private static void cleanupTransaction(Object transactionData, EntityManagerFactory emf) {
  242   		if (emf instanceof EntityManagerFactoryInfo) {
  243   			EntityManagerFactoryInfo emfInfo = (EntityManagerFactoryInfo) emf;
  244   			JpaDialect jpaDialect = emfInfo.getJpaDialect();
  245   			if (jpaDialect != null) {
  246   				jpaDialect.cleanupTransaction(transactionData);
  247   			}
  248   		}
  249   	}
  250   
  251   	/**
  252   	 * Convert the given runtime exception to an appropriate exception from the
  253   	 * <code>org.springframework.dao</code> hierarchy.
  254   	 * Return null if no translation is appropriate: any other exception may
  255   	 * have resulted from user code, and should not be translated.
  256   	 * <p>The most important cases like object not found or optimistic locking
  257   	 * failure are covered here. For more fine-granular conversion, JpaAccessor and
  258   	 * JpaTransactionManager support sophisticated translation of exceptions via a
  259   	 * JpaDialect.
  260   	 * @param ex runtime exception that occured
  261   	 * @return the corresponding DataAccessException instance,
  262   	 * or <code>null</code> if the exception should not be translated
  263   	 */
  264   	public static DataAccessException convertJpaAccessExceptionIfPossible(RuntimeException ex) {
  265   		// Following the JPA specification, a persistence provider can also
  266   		// throw these two exceptions, besides PersistenceException.
  267   		if (ex instanceof IllegalStateException) {
  268   			return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
  269   		}
  270   		if (ex instanceof IllegalArgumentException) {
  271   			return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
  272   		}
  273   
  274   		// Check for well-known PersistenceException subclasses.
  275   		if (ex instanceof EntityNotFoundException) {
  276   			return new JpaObjectRetrievalFailureException((EntityNotFoundException) ex);
  277   		}
  278   		if (ex instanceof NoResultException) {
  279   			return new EmptyResultDataAccessException(ex.getMessage(), 1);
  280   		}
  281   		if (ex instanceof NonUniqueResultException) {
  282   			return new IncorrectResultSizeDataAccessException(ex.getMessage(), 1);
  283   		}
  284   		if (ex instanceof OptimisticLockException) {
  285   			return new JpaOptimisticLockingFailureException((OptimisticLockException) ex);
  286   		}
  287   		if (ex instanceof EntityExistsException) {
  288   			return new DataIntegrityViolationException(ex.getMessage(), ex);
  289   		}
  290   		if (ex instanceof TransactionRequiredException) {
  291   			return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
  292   		}
  293   
  294   		// If we have another kind of PersistenceException, throw it.
  295   		if (ex instanceof PersistenceException) {
  296   			return new JpaSystemException((PersistenceException) ex);
  297   		}
  298   		
  299   		// If we get here, we have an exception that resulted from user code,
  300   		// rather than the persistence provider, so we return null to indicate
  301   		// that translation should not occur.
  302   		return null;				
  303   	}
  304   
  305   	/**
  306   	 * Close the given JPA EntityManager,
  307   	 * catching and logging any cleanup exceptions thrown.
  308   	 * @param em the JPA EntityManager to close (may be <code>null</code>)
  309   	 * @see javax.persistence.EntityManager#close()
  310   	 */
  311   	public static void closeEntityManager(EntityManager em) {
  312   		if (em != null) {
  313   			logger.debug("Closing JPA EntityManager");
  314   			try {
  315   				em.close();
  316   			}
  317   			catch (PersistenceException ex) {
  318   				logger.debug("Could not close JPA EntityManager", ex);
  319   			}
  320   			catch (Throwable ex) {
  321   				logger.debug("Unexpected exception on closing JPA EntityManager", ex);
  322   			}
  323   		}
  324   	}
  325   
  326   
  327   	/**
  328   	 * Callback for resource cleanup at the end of a non-JPA transaction
  329   	 * (e.g. when participating in a JtaTransactionManager transaction).
  330   	 * @see org.springframework.transaction.jta.JtaTransactionManager
  331   	 */
  332   	private static class EntityManagerSynchronization extends ResourceHolderSynchronization implements Ordered {
  333   
  334   		private final Object transactionData;
  335   
  336   		private final boolean newEntityManager;
  337   
  338   		public EntityManagerSynchronization(
  339   				EntityManagerHolder emHolder, EntityManagerFactory emf, Object transactionData, boolean newEntityManager) {
  340   			super(emHolder, emf);
  341   			this.transactionData = transactionData;
  342   			this.newEntityManager = newEntityManager;
  343   		}
  344   
  345   		public int getOrder() {
  346   			return ENTITY_MANAGER_SYNCHRONIZATION_ORDER;
  347   		}
  348   
  349   		protected boolean shouldUnbindAtCompletion() {
  350   			return this.newEntityManager;
  351   		}
  352   
  353   		protected void releaseResource(ResourceHolder resourceHolder, Object resourceKey) {
  354   			closeEntityManager(((EntityManagerHolder) resourceHolder).getEntityManager());
  355   		}
  356   
  357   		protected void cleanupResource(ResourceHolder resourceHolder, Object resourceKey, boolean committed) {
  358   			if (!committed) {
  359   				// Clear all pending inserts/updates/deletes in the EntityManager.
  360   				// Necessary for pre-bound EntityManagers, to avoid inconsistent state.
  361   				((EntityManagerHolder) resourceHolder).getEntityManager().clear();
  362   			}
  363   			cleanupTransaction(this.transactionData, (EntityManagerFactory) resourceKey);
  364   		}
  365   	}
  366   
  367   }

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