JpaTransactionManager etc can find EntityManagerFactory by "persistenceUnitName" property now, falling back to retrieval of a unique EntityManagerFactory bean by type (analogous to @PersistenceUnit / @PersistenceContext)

git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@4771 50f2f4bb-b051-0410-bef5-90022cba6387
This commit is contained in:
Juergen Hoeller 2011-07-25 12:26:27 +00:00
parent 7bfa480c99
commit dd4db60f92
5 changed files with 177 additions and 43 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2008 the original author or authors. * Copyright 2002-2011 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -19,13 +19,16 @@ package org.springframework.orm.jpa;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import javax.persistence.EntityManager; import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory; import javax.persistence.EntityManagerFactory;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
@ -40,13 +43,15 @@ import org.springframework.util.CollectionUtils;
* @see JpaAccessor * @see JpaAccessor
* @see EntityManagerFactoryUtils * @see EntityManagerFactoryUtils
*/ */
public abstract class EntityManagerFactoryAccessor { public abstract class EntityManagerFactoryAccessor implements BeanFactoryAware {
/** Logger available to subclasses */ /** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass()); protected final Log logger = LogFactory.getLog(getClass());
private EntityManagerFactory entityManagerFactory; private EntityManagerFactory entityManagerFactory;
private String persistenceUnitName;
private final Map<String, Object> jpaPropertyMap = new HashMap<String, Object>(); private final Map<String, Object> jpaPropertyMap = new HashMap<String, Object>();
@ -68,6 +73,25 @@ public abstract class EntityManagerFactoryAccessor {
return this.entityManagerFactory; return this.entityManagerFactory;
} }
/**
* Set the name of the persistence unit to access the EntityManagerFactory for.
* <p>This is an alternative to specifying the EntityManagerFactory by direct reference,
* resolving it by its persistence unit name instead. If no EntityManagerFactory and
* no persistence unit name have been specified, a default EntityManagerFactory will
* be retrieved through finding a single unique bean of type EntityManagerFactory.
* @see #setEntityManagerFactory
*/
public void setPersistenceUnitName(String persistenceUnitName) {
this.persistenceUnitName = persistenceUnitName;
}
/**
* Return the name of the persistence unit to access the EntityManagerFactory for, if any.
*/
public String getPersistenceUnitName() {
return this.persistenceUnitName;
}
/** /**
* Specify JPA properties, to be passed into * Specify JPA properties, to be passed into
* <code>EntityManagerFactory.createEntityManager(Map)</code> (if any). * <code>EntityManagerFactory.createEntityManager(Map)</code> (if any).
@ -100,6 +124,22 @@ public abstract class EntityManagerFactoryAccessor {
return this.jpaPropertyMap; return this.jpaPropertyMap;
} }
/**
* Retrieves an EntityManagerFactory by persistence unit name, if none set explicitly.
* Falls back to a default EntityManagerFactory bean if no persistence unit specified.
* @see #setPersistenceUnitName
*/
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (getEntityManagerFactory() == null) {
if (!(beanFactory instanceof ListableBeanFactory)) {
throw new IllegalStateException("Cannot retrieve EntityManagerFactory by persistence unit name " +
"in a non-listable BeanFactory: " + beanFactory);
}
ListableBeanFactory lbf = (ListableBeanFactory) beanFactory;
setEntityManagerFactory(EntityManagerFactoryUtils.findEntityManagerFactory(lbf, getPersistenceUnitName()));
}
}
/** /**
* Obtain a new EntityManager from this accessor's EntityManagerFactory. * Obtain a new EntityManager from this accessor's EntityManagerFactory.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2010 the original author or authors. * Copyright 2002-2011 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -46,6 +46,7 @@ import org.springframework.transaction.support.ResourceHolderSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
/** /**
* Helper class featuring methods for JPA EntityManager handling, * Helper class featuring methods for JPA EntityManager handling,
@ -80,7 +81,8 @@ public abstract class EntityManagerFactoryUtils {
* the persistence unit name will be matched against the Spring bean name, * the persistence unit name will be matched against the Spring bean name,
* assuming that the EntityManagerFactory bean names follow that convention. * assuming that the EntityManagerFactory bean names follow that convention.
* @param beanFactory the ListableBeanFactory to search * @param beanFactory the ListableBeanFactory to search
* @param unitName the name of the persistence unit (never empty) * @param unitName the name of the persistence unit (may be <code>null</code> or empty,
* in which case a single bean of type EntityManagerFactory will be searched for)
* @return the EntityManagerFactory * @return the EntityManagerFactory
* @throws NoSuchBeanDefinitionException if there is no such EntityManagerFactory in the context * @throws NoSuchBeanDefinitionException if there is no such EntityManagerFactory in the context
* @see EntityManagerFactoryInfo#getPersistenceUnitName() * @see EntityManagerFactoryInfo#getPersistenceUnitName()
@ -89,8 +91,7 @@ public abstract class EntityManagerFactoryUtils {
ListableBeanFactory beanFactory, String unitName) throws NoSuchBeanDefinitionException { ListableBeanFactory beanFactory, String unitName) throws NoSuchBeanDefinitionException {
Assert.notNull(beanFactory, "ListableBeanFactory must not be null"); Assert.notNull(beanFactory, "ListableBeanFactory must not be null");
Assert.hasLength(unitName, "Unit name must not be empty"); if (StringUtils.hasLength(unitName)) {
// See whether we can find an EntityManagerFactory with matching persistence unit name. // See whether we can find an EntityManagerFactory with matching persistence unit name.
String[] candidateNames = String[] candidateNames =
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, EntityManagerFactory.class); BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, EntityManagerFactory.class);
@ -106,6 +107,10 @@ public abstract class EntityManagerFactoryUtils {
// with the persistence unit name as bean name (by convention). // with the persistence unit name as bean name (by convention).
return beanFactory.getBean(unitName, EntityManagerFactory.class); return beanFactory.getBean(unitName, EntityManagerFactory.class);
} }
else {
return BeanFactoryUtils.beanOfType(beanFactory, EntityManagerFactory.class);
}
}
/** /**
* Obtain a JPA EntityManager from the given factory. Is aware of a * Obtain a JPA EntityManager from the given factory. Is aware of a

View File

@ -26,7 +26,11 @@ import javax.persistence.PersistenceException;
import javax.persistence.RollbackException; import javax.persistence.RollbackException;
import javax.sql.DataSource; import javax.sql.DataSource;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.dao.DataAccessException; import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.DataAccessUtils; import org.springframework.dao.support.DataAccessUtils;
import org.springframework.jdbc.datasource.ConnectionHandle; import org.springframework.jdbc.datasource.ConnectionHandle;
@ -105,10 +109,12 @@ import org.springframework.util.CollectionUtils;
* @see org.springframework.transaction.jta.JtaTransactionManager * @see org.springframework.transaction.jta.JtaTransactionManager
*/ */
public class JpaTransactionManager extends AbstractPlatformTransactionManager public class JpaTransactionManager extends AbstractPlatformTransactionManager
implements ResourceTransactionManager, InitializingBean { implements ResourceTransactionManager, BeanFactoryAware, InitializingBean {
private EntityManagerFactory entityManagerFactory; private EntityManagerFactory entityManagerFactory;
private String persistenceUnitName;
private final Map<String, Object> jpaPropertyMap = new HashMap<String, Object>(); private final Map<String, Object> jpaPropertyMap = new HashMap<String, Object>();
private DataSource dataSource; private DataSource dataSource;
@ -138,6 +144,10 @@ public class JpaTransactionManager extends AbstractPlatformTransactionManager
/** /**
* Set the EntityManagerFactory that this instance should manage transactions for. * Set the EntityManagerFactory that this instance should manage transactions for.
* <p>Alternatively, specify the persistence unit name of the target EntityManagerFactory.
* By default, a default EntityManagerFactory will be retrieved through finding a
* single unique bean of type EntityManagerFactory in the containing BeanFactory.
* @see #setPersistenceUnitName
*/ */
public void setEntityManagerFactory(EntityManagerFactory emf) { public void setEntityManagerFactory(EntityManagerFactory emf) {
this.entityManagerFactory = emf; this.entityManagerFactory = emf;
@ -150,6 +160,25 @@ public class JpaTransactionManager extends AbstractPlatformTransactionManager
return this.entityManagerFactory; return this.entityManagerFactory;
} }
/**
* Set the name of the persistence unit to manage transactions for.
* <p>This is an alternative to specifying the EntityManagerFactory by direct reference,
* resolving it by its persistence unit name instead. If no EntityManagerFactory and
* no persistence unit name have been specified, a default EntityManagerFactory will
* be retrieved through finding a single unique bean of type EntityManagerFactory.
* @see #setEntityManagerFactory
*/
public void setPersistenceUnitName(String persistenceUnitName) {
this.persistenceUnitName = persistenceUnitName;
}
/**
* Return the name of the persistence unit to manage transactions for, if any.
*/
public String getPersistenceUnitName() {
return this.persistenceUnitName;
}
/** /**
* Specify JPA properties, to be passed into * Specify JPA properties, to be passed into
* <code>EntityManagerFactory.createEntityManager(Map)</code> (if any). * <code>EntityManagerFactory.createEntityManager(Map)</code> (if any).
@ -247,6 +276,22 @@ public class JpaTransactionManager extends AbstractPlatformTransactionManager
return this.jpaDialect; return this.jpaDialect;
} }
/**
* Retrieves an EntityManagerFactory by persistence unit name, if none set explicitly.
* Falls back to a default EntityManagerFactory bean if no persistence unit specified.
* @see #setPersistenceUnitName
*/
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (getEntityManagerFactory() == null) {
if (!(beanFactory instanceof ListableBeanFactory)) {
throw new IllegalStateException("Cannot retrieve EntityManagerFactory by persistence unit name " +
"in a non-listable BeanFactory: " + beanFactory);
}
ListableBeanFactory lbf = (ListableBeanFactory) beanFactory;
setEntityManagerFactory(EntityManagerFactoryUtils.findEntityManagerFactory(lbf, getPersistenceUnitName()));
}
}
/** /**
* Eagerly initialize the JPA dialect, creating a default one * Eagerly initialize the JPA dialect, creating a default one
* for the specified EntityManagerFactory if none set. * for the specified EntityManagerFactory if none set.
@ -254,7 +299,7 @@ public class JpaTransactionManager extends AbstractPlatformTransactionManager
*/ */
public void afterPropertiesSet() { public void afterPropertiesSet() {
if (getEntityManagerFactory() == null) { if (getEntityManagerFactory() == null) {
throw new IllegalArgumentException("Property 'entityManagerFactory' is required"); throw new IllegalArgumentException("'entityManagerFactory' or 'persistenceUnitName' is required");
} }
if (getEntityManagerFactory() instanceof EntityManagerFactoryInfo) { if (getEntityManagerFactory() instanceof EntityManagerFactoryInfo) {
EntityManagerFactoryInfo emfInfo = (EntityManagerFactoryInfo) getEntityManagerFactory(); EntityManagerFactoryInfo emfInfo = (EntityManagerFactoryInfo) getEntityManagerFactory();

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2009 the original author or authors. * Copyright 2002-2011 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -17,7 +17,6 @@
package org.springframework.orm.jpa.support; package org.springframework.orm.jpa.support;
import java.io.IOException; import java.io.IOException;
import javax.persistence.EntityManager; import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory; import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceException; import javax.persistence.PersistenceException;
@ -27,9 +26,10 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.springframework.dao.DataAccessResourceFailureException; import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.orm.jpa.EntityManagerHolder;
import org.springframework.orm.jpa.EntityManagerFactoryUtils; import org.springframework.orm.jpa.EntityManagerFactoryUtils;
import org.springframework.orm.jpa.EntityManagerHolder;
import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.StringUtils;
import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils; import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.filter.OncePerRequestFilter;
@ -47,10 +47,10 @@ import org.springframework.web.filter.OncePerRequestFilter;
* as for non-transactional read-only execution. * as for non-transactional read-only execution.
* *
* <p>Looks up the EntityManagerFactory in Spring's root web application context. * <p>Looks up the EntityManagerFactory in Spring's root web application context.
* Supports a "entityManagerFactoryBeanName" filter init-param in <code>web.xml</code>; * Supports an "entityManagerFactoryBeanName" filter init-param in <code>web.xml</code>;
* the default bean name is "entityManagerFactory". Looks up the EntityManagerFactory * the default bean name is "entityManagerFactory". As an alternative, the
* on each request, to avoid initialization order issues (when using ContextLoaderServlet, * "persistenceUnitName" init-param allows for retrieval by logical unit name
* the root application context will get initialized <i>after</i> this filter). * (as specified in <code>persistence.xml</code>).
* *
* @author Juergen Hoeller * @author Juergen Hoeller
* @since 2.0 * @since 2.0
@ -63,15 +63,28 @@ import org.springframework.web.filter.OncePerRequestFilter;
*/ */
public class OpenEntityManagerInViewFilter extends OncePerRequestFilter { public class OpenEntityManagerInViewFilter extends OncePerRequestFilter {
public static final String DEFAULT_PERSISTENCE_MANAGER_FACTORY_BEAN_NAME = "entityManagerFactory"; /**
* Default EntityManagerFactory bean name: "entityManagerFactory".
* Only applies when no "persistenceUnitName" param has been specified.
* @see #setEntityManagerFactoryBeanName
* @see #setPersistenceUnitName
*/
public static final String DEFAULT_ENTITY_MANAGER_FACTORY_BEAN_NAME = "entityManagerFactory";
private String entityManagerFactoryBeanName = DEFAULT_PERSISTENCE_MANAGER_FACTORY_BEAN_NAME; private String entityManagerFactoryBeanName;
private String persistenceUnitName;
private volatile EntityManagerFactory entityManagerFactory;
/** /**
* Set the bean name of the EntityManagerFactory to fetch from Spring's * Set the bean name of the EntityManagerFactory to fetch from Spring's
* root application context. Default is "entityManagerFactory". * root application context.
* @see #DEFAULT_PERSISTENCE_MANAGER_FACTORY_BEAN_NAME * <p>Default is "entityManagerFactory". Note that this default only applies
* when no "persistenceUnitName" param has been specified.
* @see #setPersistenceUnitName
* @see #DEFAULT_ENTITY_MANAGER_FACTORY_BEAN_NAME
*/ */
public void setEntityManagerFactoryBeanName(String entityManagerFactoryBeanName) { public void setEntityManagerFactoryBeanName(String entityManagerFactoryBeanName) {
this.entityManagerFactoryBeanName = entityManagerFactoryBeanName; this.entityManagerFactoryBeanName = entityManagerFactoryBeanName;
@ -85,6 +98,27 @@ public class OpenEntityManagerInViewFilter extends OncePerRequestFilter {
return this.entityManagerFactoryBeanName; return this.entityManagerFactoryBeanName;
} }
/**
* Set the name of the persistence unit to access the EntityManagerFactory for.
* <p>This is an alternative to specifying the EntityManagerFactory by bean name,
* resolving it by its persistence unit name instead. If no bean name and no persistence
* unit name have been specified, we'll check whether a bean exists for the default
* bean name "entityManagerFactory"; if not, a default EntityManagerFactory will
* be retrieved through finding a single unique bean of type EntityManagerFactory.
* @see #setEntityManagerFactoryBeanName
* @see #DEFAULT_ENTITY_MANAGER_FACTORY_BEAN_NAME
*/
public void setPersistenceUnitName(String persistenceUnitName) {
this.persistenceUnitName = persistenceUnitName;
}
/**
* Return the name of the persistence unit to access the EntityManagerFactory for, if any.
*/
protected String getPersistenceUnitName() {
return this.persistenceUnitName;
}
@Override @Override
protected void doFilterInternal( protected void doFilterInternal(
@ -126,29 +160,39 @@ public class OpenEntityManagerInViewFilter extends OncePerRequestFilter {
/** /**
* Look up the EntityManagerFactory that this filter should use, * Look up the EntityManagerFactory that this filter should use,
* taking the current HTTP request as argument. * taking the current HTTP request as argument.
* <p>Default implementation delegates to the <code>lookupEntityManagerFactory</code> * <p>The default implementation delegates to the <code>lookupEntityManagerFactory</code>
* without arguments. * without arguments, caching the EntityManagerFactory reference once obtained.
* @return the EntityManagerFactory to use * @return the EntityManagerFactory to use
* @see #lookupEntityManagerFactory() * @see #lookupEntityManagerFactory()
*/ */
protected EntityManagerFactory lookupEntityManagerFactory(HttpServletRequest request) { protected EntityManagerFactory lookupEntityManagerFactory(HttpServletRequest request) {
return lookupEntityManagerFactory(); if (this.entityManagerFactory == null) {
this.entityManagerFactory = lookupEntityManagerFactory();
}
return this.entityManagerFactory;
} }
/** /**
* Look up the EntityManagerFactory that this filter should use. * Look up the EntityManagerFactory that this filter should use.
* The default implementation looks for a bean with the specified name * <p>The default implementation looks for a bean with the specified name
* in Spring's root application context. * in Spring's root application context.
* @return the EntityManagerFactory to use * @return the EntityManagerFactory to use
* @see #getEntityManagerFactoryBeanName * @see #getEntityManagerFactoryBeanName
*/ */
protected EntityManagerFactory lookupEntityManagerFactory() { protected EntityManagerFactory lookupEntityManagerFactory() {
if (logger.isDebugEnabled()) {
logger.debug("Using EntityManagerFactory '" + getEntityManagerFactoryBeanName() +
"' for OpenEntityManagerInViewFilter");
}
WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext()); WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
return wac.getBean(getEntityManagerFactoryBeanName(), EntityManagerFactory.class); String emfBeanName = getEntityManagerFactoryBeanName();
String puName = getPersistenceUnitName();
if (StringUtils.hasLength(emfBeanName)) {
return wac.getBean(emfBeanName, EntityManagerFactory.class);
}
else if (!StringUtils.hasLength(puName) && wac.containsBean(DEFAULT_ENTITY_MANAGER_FACTORY_BEAN_NAME)) {
return wac.getBean(DEFAULT_ENTITY_MANAGER_FACTORY_BEAN_NAME, EntityManagerFactory.class);
}
else {
// Includes fallback search for single EntityManagerFactory bean by type.
return EntityManagerFactoryUtils.findEntityManagerFactory(wac, puName);
}
} }
/** /**

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2009 the original author or authors. * Copyright 2002-2011 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -67,7 +67,7 @@ public class SharedEntityManagerBean extends EntityManagerFactoryAccessor
* @see javax.persistence.EntityManager * @see javax.persistence.EntityManager
*/ */
public void setEntityManagerInterface(Class<? extends EntityManager> entityManagerInterface) { public void setEntityManagerInterface(Class<? extends EntityManager> entityManagerInterface) {
Assert.notNull(entityManagerInterface, "entityManagerInterface must not be null"); Assert.notNull(entityManagerInterface, "'entityManagerInterface' must not be null");
Assert.isAssignable(EntityManager.class, entityManagerInterface); Assert.isAssignable(EntityManager.class, entityManagerInterface);
this.entityManagerInterface = entityManagerInterface; this.entityManagerInterface = entityManagerInterface;
} }
@ -76,7 +76,7 @@ public class SharedEntityManagerBean extends EntityManagerFactoryAccessor
public final void afterPropertiesSet() { public final void afterPropertiesSet() {
EntityManagerFactory emf = getEntityManagerFactory(); EntityManagerFactory emf = getEntityManagerFactory();
if (emf == null) { if (emf == null) {
throw new IllegalArgumentException("entityManagerFactory is required"); throw new IllegalArgumentException("'entityManagerFactory' or 'persistenceUnitName' is required");
} }
Class[] ifcs = null; Class[] ifcs = null;
if (emf instanceof EntityManagerFactoryInfo) { if (emf instanceof EntityManagerFactoryInfo) {