Support for "Open EntityManager in View" pattern in web applications, keeping EntityManager/Session open for the entire HTTP request to enable lazy loading in views.
Use Open EntityManager in View when:
Avoid when:
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter;
@Configuration
public class WebConfig {
@Bean
public FilterRegistrationBean<OpenEntityManagerInViewFilter>
openEntityManagerInViewFilter() {
FilterRegistrationBean<OpenEntityManagerInViewFilter> registrationBean =
new FilterRegistrationBean<>();
OpenEntityManagerInViewFilter filter = new OpenEntityManagerInViewFilter();
filter.setEntityManagerFactoryBeanName("entityManagerFactory");
registrationBean.setFilter(filter);
registrationBean.addUrlPatterns("/*");
registrationBean.setOrder(5);
return registrationBean;
}
}<filter>
<filter-name>openEntityManagerInViewFilter</filter-name>
<filter-class>
org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter
</filter-class>
<init-param>
<param-name>entityManagerFactoryBeanName</param-name>
<param-value>entityManagerFactory</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>openEntityManagerInViewFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>Alternative to filter, uses Spring MVC interceptors:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.jpa.support.OpenEntityManagerInViewInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import jakarta.persistence.EntityManagerFactory;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private EntityManagerFactory entityManagerFactory;
@Override
public void addInterceptors(InterceptorRegistry registry) {
OpenEntityManagerInViewInterceptor interceptor =
new OpenEntityManagerInViewInterceptor();
interceptor.setEntityManagerFactory(entityManagerFactory);
registry.addWebRequestInterceptor(interceptor);
}
}import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@Controller
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/users/{id}")
public String viewUser(@PathVariable Long id, Model model) {
// Load user (potentially with lazy associations)
User user = userService.findUser(id);
// EntityManager stays open, allowing lazy loading in view
model.addAttribute("user", user);
return "user-detail";
}
}<div>
<h1 th:text="${user.name}">User Name</h1>
<!-- Lazy associations can be accessed here -->
<ul>
<li th:each="order : ${user.orders}" th:text="${order.orderNumber}">
Order Number
</li>
</ul>
</div>Automatically configured by PersistenceAnnotationBeanPostProcessor (enabled by default):
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.PersistenceContext;
import jakarta.persistence.PersistenceUnit;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserService {
// Inject shared EntityManager proxy
@PersistenceContext
private EntityManager entityManager;
// Inject specific persistence unit
@PersistenceContext(unitName = "secondaryPU")
private EntityManager secondaryEntityManager;
// Inject extended EntityManager (for stateful beans)
@PersistenceContext(type = PersistenceContextType.EXTENDED)
private EntityManager extendedEntityManager;
// Inject EntityManagerFactory
@PersistenceUnit
private EntityManagerFactory entityManagerFactory;
@Transactional(readOnly = true)
public User findUser(Long id) {
return entityManager.find(User.class, id);
}
}For explicit shared EntityManager bean:
import org.springframework.orm.jpa.support.SharedEntityManagerBean;
@Configuration
public class JpaConfig {
@Bean
public SharedEntityManagerBean sharedEntityManager(
EntityManagerFactory entityManagerFactory) {
SharedEntityManagerBean sharedEntityManager = new SharedEntityManagerBean();
sharedEntityManager.setEntityManagerFactory(entityManagerFactory);
sharedEntityManager.setSynchronizedWithTransaction(true);
return sharedEntityManager;
}
}Usage:
@Service
public class UserService {
private final EntityManager entityManager;
public UserService(@Qualifier("sharedEntityManager") EntityManager entityManager) {
this.entityManager = entityManager;
}
@Transactional(readOnly = true)
public User findUser(Long id) {
return entityManager.find(User.class, id);
}
}Benefits:
Considerations:
Recommended approach instead of Open EntityManager in View:
@Service
@Transactional(readOnly = true)
public class UserService {
@PersistenceContext
private EntityManager entityManager;
// Fetch associations explicitly
public User findUserWithOrders(Long id) {
return entityManager.createQuery(
"SELECT u FROM User u LEFT JOIN FETCH u.orders WHERE u.id = :id",
User.class)
.setParameter("id", id)
.getSingleResult();
}
}class OpenEntityManagerInViewFilter {
// Configuration
void setEntityManagerFactoryBeanName(String emfBeanName);
String getEntityManagerFactoryBeanName();
void setPersistenceUnitName(String persistenceUnitName);
String getPersistenceUnitName();
}class OpenEntityManagerInViewInterceptor {
// Inherited from EntityManagerFactoryAccessor
void setEntityManagerFactory(EntityManagerFactory emf);
EntityManagerFactory getEntityManagerFactory();
void setPersistenceUnitName(String name);
String getPersistenceUnitName();
void setJpaProperties(Properties properties);
void setJpaPropertyMap(Map<String, Object> properties);
Map<String, Object> getJpaPropertyMap();
// Web request handling
void preHandle(WebRequest request);
void afterCompletion(WebRequest request, Exception ex);
}class SharedEntityManagerBean {
// Configuration
void setEntityManagerFactory(EntityManagerFactory emf);
void setPersistenceUnitName(String name);
void setEntityManagerInterface(Class<? extends EntityManager> emInterface);
void setSynchronizedWithTransaction(boolean synchronized);
// Factory bean methods
EntityManager getObject();
Class<? extends EntityManager> getObjectType();
}