Announcement Announcement Module
Collapse
No announcement yet.
Roo and LazyInitializationException Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Roo and LazyInitializationException

    Hi

    In our new Spring based web project I decided to give Roo a try.
    It looked great until I faced with a couple of problems, here is the first:

    I created an entity named OPUser, here is the relevant part of it:

    @Entity
    @RooJavaBean
    @RooToString
    @RooEntity(finders = { "findOPUsersByEmailEquals" })
    public class OPUser implements UserDetails {

    @Column(name = "email", unique = true, nullable = false, length = 64)
    private String email;

    @Column(name = "password", unique = false, nullable = false, length = 64)
    private String password;

    @OneToMany(targetEntity = Something.class, mappedBy = "opuser")
    private Set<Something> things;

    ...
    }

    and I have my AuthenticationService:

    @Service
    public class AuthenticationService implements UserDetailsService {

    public AuthenticationService() { }

    @Override
    public UserDetails loadUserByUsername(String username)
    throws UsernameNotFoundException, DataAccessException {
    try {
    OPUser user = (OPUser)OPUser.findOPUsersByEmailEquals(username). getSingleResult();
    System.out.println(user);
    return user;
    }
    catch (NoResultException e) {
    throw new UsernameNotFoundException("No such user");
    }
    catch (NonUniqueResultException e) {
    throw new RuntimeException("duplicate email, must not happend!");
    }
    }
    }

    Unfortunatelly the System.out.println line throws LazyInitializationException because the set is lazy loaded
    If I mark the method with @Transactional it works ...

    Ok, I removed the System.out line and I tried to use this object from jsp.

    From jsp I wanted to access the logged in user's object and I could not find a simple solution for this Is there any ? I could not do it with Spring Security tag library (auth: ) so I did the following:

    <jsp:root xmlns:c="http://java.sun.com/jsp/jstl/core" xmlns:spring="http://www.springframework.org/tags" xmlns:jsp="http://java.sun.com/JSP/Page" version="2.0">
    <jsp:useBean id="user" class="OPUserHelper" scope="application" type="OPUserHelper"></jsp:useBean>
    <c:forEach items="${user.OPUser.things}" var="thing">
    ${thing.name}
    </c:forEach>
    </jsp:root>

    and OPUserHelper is:
    @Configurable
    public class OPUserHelper {

    @PersistenceContext
    transient EntityManager entityManager;

    public OPUser getOPUser() {
    try {
    Object o = SecurityContextHolder.getContext().getAuthenticati on().getPrincipal();

    if (o instanceof OPUser) {

    OPUser u=(OPUser)o;
    return u;
    } catch (Throwable th) {
    System.err.println(th);
    th.printStackTrace();
    }
    return null;
    }
    }

    From jsp I also receive LazyInitializationException unless before returning the object in OPUserHelper I do the following:
    ((Session)entityManager.getDelegate()).refresh(u);
    Unfortunatelly this rereads the object from the database and I'd like to avoid that.

    Is there a simple way to have a Roo object as UserDetails and reuse it in subsequent requests and avoiding both LazyInitializationException and rereading from database ??

    Thanks

    Gabor

  • #2
    This is strange, as in web.xml we setup the OpenEntityManagerInViewFilter so you should never see LazyInitializationExceptions. Also, by the time Spring Security has authenticated, it will have touched the association and therefore the object should be fully initialized by the time it is put into the SecurityContextHolder.

    Have you changed any settings in web.xml, such as the OpenEntityManagerInViewFilter filter mapping or filter declaration? Also, which version of Spring Security are you using?

    I would recommend you put @Transactional on the AuthenticationService.loadUserByUsername(String) method and then just have a method that calls user.getThings() (no need to System.out.println() it, just call the method to cause the lazy initialization to occur). That way by the time loadUserbyUsername returns the object is fully initialized and it will be stored in the SecurityContextHolder in that form.

    Please let us know how you go with this. I hope this solves it for you.

    Cheers
    Ben

    Comment


    • #3
      Dear Ben,

      First ot all thanks you for caring for my problem and taking time to help me.
      In separate threads I'll report my other problems

      Originally posted by Ben Alex View Post
      This is strange, as in web.xml we setup the OpenEntityManagerInViewFilter so you should never see LazyInitializationExceptions. Also, by the time Spring Security has authenticated, it will have touched the association and therefore the object should be fully initialized by the time it is put into the SecurityContextHolder.
      Sorry but I can not see what Spring Security has to to with the associations... It calls loadUserByUsername but I guess it has no knowledge on the internals of the UserDetails and UserDetailsService...

      Originally posted by Ben Alex View Post
      Have you changed any settings in web.xml, such as the OpenEntityManagerInViewFilter filter mapping or filter declaration? Also, which version of Spring Security are you using?
      I have that filter in web.xml. I use Spring Core 3.0.0RC3 and Spring Security 3.0.0RC1.

      Originally posted by Ben Alex View Post
      I would recommend you put @Transactional on the AuthenticationService.loadUserByUsername(String) method and then just have a method that calls user.getThings() (no need to System.out.println() it, just call the method to cause the lazy initialization to occur). That way by the time loadUserbyUsername returns the object is fully initialized and it will be stored in the SecurityContextHolder in that form.
      Well of course this is a trivial solution but suboptimal. I might have many associations and I don't want to initialize them manually so eager fetching might be better (not to mention that with eager fetching I end up with fewer number of sql selects). But I'd like to use lazy loading if possible.
      Also, putting @Transactional on loadUserByUsername seems strange to me.

      Imho OpenEntityManagerInViewFilter is no panacea for LazyLoadingException. It helps when you open the entity in a controller and use it in a view but I might very wrong about this...

      In my case I load the entity when the authentication takes place (the authentication form is submitted) and the not fully initialized object is stored in SecurityContext. In a separate request I try to use this object and the entitymanager for this second request throws the LazyInitializationException.

      I'll create a small project from scratch to demonstrate this.

      again, thanks for helping

      Gabor

      Comment


      • #4
        You could always instruct JPA to load eagerly:

        Code:
        @OneToMany(cascade = CascadeType.ALL, mappedBy = "opuser",fetch=FetchType.EAGER)
        But a complete application to see what you are doing is probably more helpful.

        -Stefan

        Comment


        • #5
          The core problem I suspect is this...

          When Spring Security initially authenticates the user, it calls UserDetailsService.loadUserByUsername(String). The UserDetails is returned. This is put inside an Authentication object. The Authentication object is put inside a SecurityContext and that in turn is put into the SecurityContextHolder. At this time it's still a good old Hibernate lazily loaded object; all is good. But now Spring Security's HTTP session integration will kick in and it will copy the SecurityContext into the HTTP session. In a subsequent request, the SecurityContext will be pulled from the HTTP session and put back into the SecurityContextHolder. Now if you try to access a UserDetails association at this point, it will fail because the original Hibernate Session that retrieved the UserDetails is long gone (it was in an earlier HTTP request).

          So the only approach I can see that would work across multiple HTTP calls is touching the associations when you retrieve UserDetails or use eager fetching as Stefan suggested.

          You might like to ask on the Spring Security forum for help on this issue, as many people use Hibernate to store their UserDetails objects. Roo isn't doing anything that would make it harder (to the contrary, Roo makes it easier by making it quicker to develop a persistent UserDetails entity and also setting up OpenEntityManagerInView).

          Comment


          • #6
            better solution ?

            Dear Bean and Stefan,

            Roo isn't doing anything that would make it harder (to the contrary, Roo makes it easier by making it quicker to develop a persistent UserDetails entity and also setting up OpenEntityManagerInView).
            I'm sure Roo is intended to make life easier

            Manual set initialization should work but ugly. Eager loading also works but I'd like to avoid it because I don't want to load the whole damn thing unless necessary.

            I wondered if the problem could be solved with a nice aspect and worked on it a whole day, and during googling I found this:
            http://9mmedia.com/blog/?p=272

            It works perfectly. This feature imho should be part of Roo as it really makes things much easier.

            Have a nice day

            Gabor

            Comment


            • #7
              This could be a potentially good use case for the Spring Aspects project, but using the standard JPA EntityManager for merge instead of Hibernate-specific APIs. If you'd like, you're welcome to log this as an enhancement request against Spring Aspects. Thanks for suggestion.

              Comment

              Working...
              X