or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

exception-translation.mdhibernate-configuration.mdhibernate-transaction-management.mdindex.mdjpa-configuration.mdjpa-transaction-management.mdjpa-vendor-adapters.mdpersistence-unit-management.mdshared-resources.mdutility-classes.mdweb-integration.md
tile.json

web-integration.mddocs/

Web Integration

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.

Quick Decision

Use Open EntityManager in View when:

  • Need lazy loading in views (JSP, Thymeleaf, etc.)
  • Simple read-mostly applications
  • Prototyping/development

Avoid when:

  • High-performance requirements
  • Complex data access patterns
  • Can use explicit fetching strategies

Filter Configuration (Java Config)

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 Configuration (web.xml)

<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>

Interceptor Configuration

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);
    }
}

Usage in Controllers

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";
    }
}

View Example (Thymeleaf)

<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>

@PersistenceContext and @PersistenceUnit Injection

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);
    }
}

Shared EntityManager Factory Bean

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);
    }
}

Pattern Benefits and Considerations

Benefits:

  • Enables lazy loading in views
  • Simplifies data access layer
  • Avoids LazyInitializationException

Considerations:

  • Should use read-only transactions for views
  • Can mask data access design issues
  • May cause N+1 query problems
  • Prefer explicit fetching strategies for better performance

Alternative: Explicit Fetch Joins

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();
    }
}

API Reference

OpenEntityManagerInViewFilter

class OpenEntityManagerInViewFilter {

    // Configuration
    void setEntityManagerFactoryBeanName(String emfBeanName);
    String getEntityManagerFactoryBeanName();

    void setPersistenceUnitName(String persistenceUnitName);
    String getPersistenceUnitName();
}

OpenEntityManagerInViewInterceptor

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);
}

SharedEntityManagerBean

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();
}

Related Topics

  • JPA Configuration - EntityManagerFactory setup
  • Shared Resources - Shared EntityManager proxies