001/* 002 * Copyright 2002-2017 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.orm.jpa.support; 018 019import java.io.IOException; 020 021import javax.persistence.EntityManager; 022import javax.persistence.EntityManagerFactory; 023import javax.persistence.PersistenceException; 024import javax.servlet.FilterChain; 025import javax.servlet.ServletException; 026import javax.servlet.http.HttpServletRequest; 027import javax.servlet.http.HttpServletResponse; 028 029import org.springframework.dao.DataAccessResourceFailureException; 030import org.springframework.lang.Nullable; 031import org.springframework.orm.jpa.EntityManagerFactoryUtils; 032import org.springframework.orm.jpa.EntityManagerHolder; 033import org.springframework.transaction.support.TransactionSynchronizationManager; 034import org.springframework.util.StringUtils; 035import org.springframework.web.context.WebApplicationContext; 036import org.springframework.web.context.request.async.CallableProcessingInterceptor; 037import org.springframework.web.context.request.async.WebAsyncManager; 038import org.springframework.web.context.request.async.WebAsyncUtils; 039import org.springframework.web.context.support.WebApplicationContextUtils; 040import org.springframework.web.filter.OncePerRequestFilter; 041 042/** 043 * Servlet Filter that binds a JPA EntityManager to the thread for the 044 * entire processing of the request. Intended for the "Open EntityManager in 045 * View" pattern, i.e. to allow for lazy loading in web views despite the 046 * original transactions already being completed. 047 * 048 * <p>This filter makes JPA EntityManagers available via the current thread, 049 * which will be autodetected by transaction managers. It is suitable for service 050 * layer transactions via {@link org.springframework.orm.jpa.JpaTransactionManager} 051 * or {@link org.springframework.transaction.jta.JtaTransactionManager} as well 052 * as for non-transactional read-only execution. 053 * 054 * <p>Looks up the EntityManagerFactory in Spring's root web application context. 055 * Supports an "entityManagerFactoryBeanName" filter init-param in {@code web.xml}; 056 * the default bean name is "entityManagerFactory". As an alternative, the 057 * "persistenceUnitName" init-param allows for retrieval by logical unit name 058 * (as specified in {@code persistence.xml}). 059 * 060 * @author Juergen Hoeller 061 * @since 2.0 062 * @see OpenEntityManagerInViewInterceptor 063 * @see org.springframework.orm.jpa.JpaTransactionManager 064 * @see org.springframework.orm.jpa.SharedEntityManagerCreator 065 * @see org.springframework.transaction.support.TransactionSynchronizationManager 066 */ 067public class OpenEntityManagerInViewFilter extends OncePerRequestFilter { 068 069 /** 070 * Default EntityManagerFactory bean name: "entityManagerFactory". 071 * Only applies when no "persistenceUnitName" param has been specified. 072 * @see #setEntityManagerFactoryBeanName 073 * @see #setPersistenceUnitName 074 */ 075 public static final String DEFAULT_ENTITY_MANAGER_FACTORY_BEAN_NAME = "entityManagerFactory"; 076 077 078 @Nullable 079 private String entityManagerFactoryBeanName; 080 081 @Nullable 082 private String persistenceUnitName; 083 084 @Nullable 085 private volatile EntityManagerFactory entityManagerFactory; 086 087 088 /** 089 * Set the bean name of the EntityManagerFactory to fetch from Spring's 090 * root application context. 091 * <p>Default is "entityManagerFactory". Note that this default only applies 092 * when no "persistenceUnitName" param has been specified. 093 * @see #setPersistenceUnitName 094 * @see #DEFAULT_ENTITY_MANAGER_FACTORY_BEAN_NAME 095 */ 096 public void setEntityManagerFactoryBeanName(@Nullable String entityManagerFactoryBeanName) { 097 this.entityManagerFactoryBeanName = entityManagerFactoryBeanName; 098 } 099 100 /** 101 * Return the bean name of the EntityManagerFactory to fetch from Spring's 102 * root application context. 103 */ 104 @Nullable 105 protected String getEntityManagerFactoryBeanName() { 106 return this.entityManagerFactoryBeanName; 107 } 108 109 /** 110 * Set the name of the persistence unit to access the EntityManagerFactory for. 111 * <p>This is an alternative to specifying the EntityManagerFactory by bean name, 112 * resolving it by its persistence unit name instead. If no bean name and no persistence 113 * unit name have been specified, we'll check whether a bean exists for the default 114 * bean name "entityManagerFactory"; if not, a default EntityManagerFactory will 115 * be retrieved through finding a single unique bean of type EntityManagerFactory. 116 * @see #setEntityManagerFactoryBeanName 117 * @see #DEFAULT_ENTITY_MANAGER_FACTORY_BEAN_NAME 118 */ 119 public void setPersistenceUnitName(@Nullable String persistenceUnitName) { 120 this.persistenceUnitName = persistenceUnitName; 121 } 122 123 /** 124 * Return the name of the persistence unit to access the EntityManagerFactory for, if any. 125 */ 126 @Nullable 127 protected String getPersistenceUnitName() { 128 return this.persistenceUnitName; 129 } 130 131 132 /** 133 * Returns "false" so that the filter may re-bind the opened {@code EntityManager} 134 * to each asynchronously dispatched thread and postpone closing it until the very 135 * last asynchronous dispatch. 136 */ 137 @Override 138 protected boolean shouldNotFilterAsyncDispatch() { 139 return false; 140 } 141 142 /** 143 * Returns "false" so that the filter may provide an {@code EntityManager} 144 * to each error dispatches. 145 */ 146 @Override 147 protected boolean shouldNotFilterErrorDispatch() { 148 return false; 149 } 150 151 @Override 152 protected void doFilterInternal( 153 HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) 154 throws ServletException, IOException { 155 156 EntityManagerFactory emf = lookupEntityManagerFactory(request); 157 boolean participate = false; 158 159 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); 160 String key = getAlreadyFilteredAttributeName(); 161 162 if (TransactionSynchronizationManager.hasResource(emf)) { 163 // Do not modify the EntityManager: just set the participate flag. 164 participate = true; 165 } 166 else { 167 boolean isFirstRequest = !isAsyncDispatch(request); 168 if (isFirstRequest || !applyEntityManagerBindingInterceptor(asyncManager, key)) { 169 logger.debug("Opening JPA EntityManager in OpenEntityManagerInViewFilter"); 170 try { 171 EntityManager em = createEntityManager(emf); 172 EntityManagerHolder emHolder = new EntityManagerHolder(em); 173 TransactionSynchronizationManager.bindResource(emf, emHolder); 174 175 AsyncRequestInterceptor interceptor = new AsyncRequestInterceptor(emf, emHolder); 176 asyncManager.registerCallableInterceptor(key, interceptor); 177 asyncManager.registerDeferredResultInterceptor(key, interceptor); 178 } 179 catch (PersistenceException ex) { 180 throw new DataAccessResourceFailureException("Could not create JPA EntityManager", ex); 181 } 182 } 183 } 184 185 try { 186 filterChain.doFilter(request, response); 187 } 188 189 finally { 190 if (!participate) { 191 EntityManagerHolder emHolder = (EntityManagerHolder) 192 TransactionSynchronizationManager.unbindResource(emf); 193 if (!isAsyncStarted(request)) { 194 logger.debug("Closing JPA EntityManager in OpenEntityManagerInViewFilter"); 195 EntityManagerFactoryUtils.closeEntityManager(emHolder.getEntityManager()); 196 } 197 } 198 } 199 } 200 201 /** 202 * Look up the EntityManagerFactory that this filter should use, 203 * taking the current HTTP request as argument. 204 * <p>The default implementation delegates to the {@code lookupEntityManagerFactory} 205 * without arguments, caching the EntityManagerFactory reference once obtained. 206 * @return the EntityManagerFactory to use 207 * @see #lookupEntityManagerFactory() 208 */ 209 protected EntityManagerFactory lookupEntityManagerFactory(HttpServletRequest request) { 210 EntityManagerFactory emf = this.entityManagerFactory; 211 if (emf == null) { 212 emf = lookupEntityManagerFactory(); 213 this.entityManagerFactory = emf; 214 } 215 return emf; 216 } 217 218 /** 219 * Look up the EntityManagerFactory that this filter should use. 220 * <p>The default implementation looks for a bean with the specified name 221 * in Spring's root application context. 222 * @return the EntityManagerFactory to use 223 * @see #getEntityManagerFactoryBeanName 224 */ 225 protected EntityManagerFactory lookupEntityManagerFactory() { 226 WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext()); 227 String emfBeanName = getEntityManagerFactoryBeanName(); 228 String puName = getPersistenceUnitName(); 229 if (StringUtils.hasLength(emfBeanName)) { 230 return wac.getBean(emfBeanName, EntityManagerFactory.class); 231 } 232 else if (!StringUtils.hasLength(puName) && wac.containsBean(DEFAULT_ENTITY_MANAGER_FACTORY_BEAN_NAME)) { 233 return wac.getBean(DEFAULT_ENTITY_MANAGER_FACTORY_BEAN_NAME, EntityManagerFactory.class); 234 } 235 else { 236 // Includes fallback search for single EntityManagerFactory bean by type. 237 return EntityManagerFactoryUtils.findEntityManagerFactory(wac, puName); 238 } 239 } 240 241 /** 242 * Create a JPA EntityManager to be bound to a request. 243 * <p>Can be overridden in subclasses. 244 * @param emf the EntityManagerFactory to use 245 * @see javax.persistence.EntityManagerFactory#createEntityManager() 246 */ 247 protected EntityManager createEntityManager(EntityManagerFactory emf) { 248 return emf.createEntityManager(); 249 } 250 251 private boolean applyEntityManagerBindingInterceptor(WebAsyncManager asyncManager, String key) { 252 CallableProcessingInterceptor cpi = asyncManager.getCallableInterceptor(key); 253 if (cpi == null) { 254 return false; 255 } 256 ((AsyncRequestInterceptor) cpi).bindEntityManager(); 257 return true; 258 } 259 260}