3

I am using Hibernate with Spring and i run into this problem failed to lazily initialize a collection : , no session or session was closed using one-to-many relationship I have two tables one is Users & Roles from Users i am trying to get the roles using lazy fetch. The StackTrace

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.rep.users.KZ_Users.roleList, no session or session was closed
    org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:358)
    org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:350)
    org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:343)
    org.hibernate.collection.AbstractPersistentCollection.read(AbstractPersistentCollection.java:86)
    org.hibernate.collection.PersistentList.iterator(PersistentList.java:115)
    com.rep.users.KZ_Users.getAuthorities(KZ_Users.java:137)
    org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.createSuccessAuthentication(AbstractUserDetailsAuthenticationProvider.java:186)
    org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.authenticate(AbstractUserDetailsAuthenticationProvider.java:165)
    org.springframework.security.authentication.ProviderManager.doAuthentication(ProviderManager.java:120)
    org.springframework.security.authentication.AbstractAuthenticationManager.authenticate(AbstractAuthenticationManager.java:48)
    org.springframework.security.authentication.ProviderManager.doAuthentication(ProviderManager.java:138)
    org.springframework.security.authentication.AbstractAuthenticationManager.authenticate(AbstractAuthenticationManager.java:48)
    org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.attemptAuthentication(UsernamePasswordAuthenticationFilter.java:97)
    org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
    org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:105)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
    org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:79)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
    org.springframework.security.web.session.ConcurrentSessionFilter.doFilter(ConcurrentSessionFilter.java:109)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
    org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:149)
    org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:343)
    org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:260)

i have implemented UserDetails, Serializable in my KZ_Users pojo class and the method that causing the error is

@Override
    public Collection<GrantedAuthority> getAuthorities() {
        List<GrantedAuthority> list = new ArrayList<GrantedAuthority>();

        for (Roles roles : roleList) { //<-- ERROR 
            list.add(new GrantedAuthorityImpl(roles.getRole()));
        }

        return list;
    }

the rolelist is define in KZUser Class. Can any one tell me what was the problem Error is

 16:15:01,317 ERROR [org.apache.catalina.core.ContainerBase.[jboss.web].[default-host].[/ReportingPortalV3].[default]] (http-/192.168.1.124:8080-2) JBWEB000236: Servlet.service() for servlet default threw exception: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.rep.users.KZ_Users.roleList, no session or session was closed

Thanks in advance.

RizN81
  • 1,009
  • 15
  • 26
  • The error says it all. You are accessing a collection that isn't initialised and your session is closed. Either force init of the collection while the session is open or keep the session open. You don't show any code related to session management... – Boris the Spider Mar 22 '14 at 10:55
  • Yes i figure it out but the method `getAuthorities()`is called by Spring so i dont know how and where i initialized it – RizN81 Mar 22 '14 at 10:57
  • Well, in that case all the hibernate mappings are irrelevant, remove them from the post. Add the full stack trace of the error and explain how the method is called. – Boris the Spider Mar 22 '14 at 10:58
  • In your hibernate Mapping lazy = ture. Make it false and try. – Shekhar Khairnar Mar 22 '14 at 11:09
  • @Shekhar more bad advice? Setting the collection to `lazy="false"` will load the whole collection **whenever** a `KZ_Users` users class is loaded. This is a dirty hack not a solution. Although admittedly it is more likely to work than setting `cascade="all"`. – Boris the Spider Mar 22 '14 at 11:12
  • yes @Shekhar i dont want to load whole collection – RizN81 Mar 22 '14 at 11:20
  • Well @BoristheSpider i have added full stack trace of error and one thing i figure out it the spring call `getAuthorities()` internally from one of its abstract class `AbstractUserDetailsAuthenticationProvider` from `createSuccessAuthentication() ` method – RizN81 Mar 22 '14 at 11:28
  • 1
    @RizN81 so this method is in a bean used by `Spring`? Simple, annotate it with `@Transactional` and enable [annotation based transaction management](http://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/transaction.html). – Boris the Spider Mar 22 '14 at 11:33
  • @BoristheSpider yes this method used by Spring – RizN81 Mar 22 '14 at 12:09

1 Answers1

1

Keep Spring specific API separate from your entities. Instead of directly implementing UserDetails interface in your @Entity class, extend it (or compose), i.e.:

public class CurrentUser extends User implements UserDetails {

    private final Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();

    public CurrentUser(User user) {
        super(user);
        initAuthorities(user);
    }

    private void initAuthorities(User user) {
        if (user.getRoles() == null) {
            return;
        }
        for (Authority role : user.getRoles()) {
            authorities.add(new SimpleGrantedAuthority(role.getAuthority()));
        }
    }

    @Override
    public Collection<GrantedAuthority> getAuthorities() {
        return authorities;
    }

    // implement the rest of UserDetails interface accordingly
}

Also keep initialization of authorities collection in the constructor. And then:

public class UserDetailsServiceImpl extends UserDetailsService {

    @Override
    @Transactional(propagation = SUPPORTS, readOnly = true)
    public CurrentUser loadUserByUsername(String username) 
            throws AuthenticationException ,DataAccessException {
        ensureNotEmpty(username);
        User user = userRepository.findUserWithAuthorities(username);
        if (user == null) {
            throw new UsernameNotFoundException("Invalid credentials!");
        }
        return new CurrentUser(user);
    }
}

Notice that this way you'll be accessing the roles collection still within the transaction marked my @Transactional. Now Spring can call getAuthorities() wherewer it needs, and it will work fine, bacuse it's already initialized and connection to database is no longer required.

Always know where your transaction starts and ends.

Depending on your use case, you migh also think about using OpenEntityManagerInViewFilter or OpenEntityManagerInViewInterceptor (or equivalents for native Hibernate API OpenSessionInViewFilter/Intercetor), but read about consequences first.

And please tell me that KZ in the name of the class are not your initiials? If they are, please, please, please don't do that...

Community
  • 1
  • 1
Roadrunner
  • 6,661
  • 1
  • 29
  • 38
  • Thanks For your answer and Yes `KZ`is not my initials its name of database schema – RizN81 Mar 23 '14 at 04:49
  • @Roadrunner I'm having the exact same problem. I tried just what you suggested in your answer but I'm still having the same exception. My [UserDetailsServiceImpl is here](https://codeshare.io/Gkn4yw) – Sebastiandg7 Oct 22 '17 at 22:35
  • @SebastianDuque it's all about knowing the boundaries of your transaction and knowing how Hibernate lazy loading works; i.e. in your code line 27 will NOT fetch roles list; you'd have to iterate throgh it to load it; I could assume you're not initializing the reoles in `UserDetailsImpl` constructor, but later, after the transaction is closed; but I'd have to see the full stack trace to know that; – Roadrunner Oct 26 '17 at 13:08
  • @Roadrunner Yes, just became UserDetailsServiceImpl a Component and the transaction scope now is the whole loadUserByUsername() method. Thank you a lot. – Sebastiandg7 Oct 28 '17 at 00:33