Spring Boot Data JPA: Hibernate Session issue

12,267

Solution 1

If you want to be able to access mapped entities outside the transaction (which you seem to be doing), you need to flag it as an "eager" join. i.e.

@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)

Solution 2

This question has been answered beautifully by @Steve. However, if you still want to maintain your lazy loading implementation, you may want to try this

import javax.servlet.Filter;

import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.hibernate4.support.OpenSessionInViewFilter;  

@Configuration
@ComponentScan
public class AppConfig {

    @Bean
    public FilterRegistrationBean filterRegistration() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(openSessionInView());
        registration.addUrlPatterns("/*");

        return registration;
    }

    @Bean
    public Filter openSessionInView() {
        return new OpenSessionInViewFilter();
    }
}

What this configuration does is, it registers a Filter on requests to path "/*" which keeps your Hibernate Session open in your view.

This is an anti-pattern and must be used with care.

NOTE: As of Spring Boot 1.3.5.RELEASE, when you use the default configuration with Spring Data JPA auto-configuration, you shouldn't encounter this problem

Solution 3

I faced similar issue in spring boot application, after googling I'm able to fix this issue by adding the following code to my application.

    @Bean(name = "entityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder, DataSource dataSource) {
    LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
    entityManagerFactoryBean.setDataSource(dataSource);
    entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
    Properties jpaProperties = new Properties();
    jpaProperties.put("hibernate.enable_lazy_load_no_trans", true);
    entityManagerFactoryBean.setJpaProperties(jpaProperties);
    return entityManagerFactoryBean;
}

Referred here.

Share:
12,267

Related videos on Youtube

Alan47
Author by

Alan47

Updated on June 04, 2022

Comments

  • Alan47
    Alan47 almost 2 years

    I'm developing a Spring Boot based web application. I heavily rely on @ComponentScan and @EnableAutoConfiguration and no explicit XML configuration in place.

    I have the following problem. I have a JPA-Annotated Entity class called UserSettings:

    @Entity public class UserSettings {
    
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        private long id;
    
        @OneToMany(cascade = CascadeType.ALL)
        private Set<Preference> preferences; // 'Preference' is another @Entity class
    
        public UserSettings() {
             this.preferences = new HashSet<Preference>();
        }
    
    // some more primitive properties, Getters, Setters...
    }
    

    I followed this tutorial and created a repository interface that extends JpaRepository<UserSettings,Long>.

    Furthermore, I have a UserManager bean:

    @Component public class SettingsManager {
    
    @Autowired
    UserSettingsRepository settingsRepository;
    
    @PostConstruct
    protected void init() {
        // 'findGlobalSettings' is a simple custom HQL query 
        UserSettings globalSettings = this.settingsRepository.findGlobalSettings();
        if (globalSettings == null) {
            globalSettings = new UserSettings();
            this.settingsRepository.saveAndFlush(globalSettings);
        }
    }
    

    Later in the code, I load the UserSettings object created here, again with the findGlobalSetttings query.

    The problem is: Every time I try to access the @OneToMany attribute of the settings object, I get the following exception:

    org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role org.example.UserSettings.preferences, could not initialize proxy - no Session

    I understand that each HTTP Session has its own Hibernate Session, as described in the accepted answer of this question, but that does not apply in my case (currently I'm testing this within the same HTTP Session), which is why I have no idea where this exception comes from.

    What am I doing wrong and how can I achieve circumvent the error?

    • M. Deinum
      M. Deinum almost 10 years
      A Hibernate Session is not a Http Session. As soon as yuor transaction is over the hibernate session is closed. Not is a hibernate session attached to a http session. It is attached to the current transaction (or when using it with open session/entitymnager in view the request).
    • Alan47
      Alan47 almost 10 years
      Thanks for pointing this out. However, I believe that the error resides somewhere in the implementation of the JpaRepository class that is auto-generated by Spring at runtime. I just tried to retrieve the desired instance directly by using the EntityManager - which works perfectly fine. Doing the same retrieval with the JpaRepository yields an object of the same class with the same ID (!) but that object is different with respect to the == operator to the object returned by the entity manager...
    • M. Deinum
      M. Deinum almost 10 years
      No it isn't it is the same problem and has nothing to do with Spring Data. Retrieving the object from 2 different entitymanagers will always get you another object. The problem is simple as you store a reference in the http session, the original hibernate session is gone when you try to do things on that entity from the session. Either retrieve it each time you need it or fully initialize your object before putting it in the session.
    • Alan47
      Alan47 almost 10 years
      I just double checked it: the underlying Hibernate 'Session' object has the very same Java Object ID when storing my UserSettings object as it does when retrieving the UserSettings object. As far as I can tell (I'm using Vaadin as a web UI toolkit and Vaadin4Spring), I'm still in the same HTTP session and request, too. Regardless, I get a different UserSettings object when storing and querying for it and I get the exception stated above.
    • M. Deinum
      M. Deinum almost 10 years
      As stated twice before the session is closed as soon as the transaction ends unless you are using open session in view which will maintain the hibernate session until the view is rendered. Else the hibernate session is gone as soon as the tx is done.
  • Steve
    Steve almost 10 years
    If you haven't used it before, it's probably worth noting that by flagging a join as eager, JPA/Hibernate will add the join to all queries and generate the joined objects. As you might imagine, over-use can lead to big slow queries which generate huge graphs of entities. So just be careful not to use it everywhere. :)
  • Alan47
    Alan47 almost 10 years
    Yes, I thought so. I will be careful about it. Currently, the domain model in my application is very simple and the element reachability closures are rather small too, so I guess that I can live with it for now. If the performance hit becomes noticeable, I will have to think of something else anyways. Thanks for pointing this out.
  • Steve
    Steve almost 10 years
    btw ... If it does become a problem, the usual alternative is to create a @Transactional method, which returns a full model of what will be rendered on the page, rather than passing your entities to whatever does your view rendering. Either that or you can just iterate through lazy-loaded collections inside a @Transactional method, to force them to be generated.
  • Alan47
    Alan47 almost 10 years
    I'll make sure to remember that, could come in handy. Our data model is rather simple (at least the part which is O/R mapped) but the application itself is rather complex. So perhaps we will need to make use of this soon. Thanks!
  • Rocky Hu
    Rocky Hu about 7 years
    OpenEntityManagerInViewInterceptor is auto to be registered, but it's not work when the logic is happened in the filter, for me, I register the OpenEntityManagerInViewFilter and it solved my problem.