Help

Built with Seam

You can find the full source code for this website in the Seam package in the directory /examples/wiki. It is licensed under the LGPL.

Seam solves the problem of LazyInitializationExceptions (LIE) for Conversations using a Seam Managed Persistence Context (SMPC). Seam's EntityHomes make it trivial to solve this problem for longer scopes - SESSION, APPLICATION and BUSINESS_PROCESS.

How to use it

@Name("currentUserHome")
public class CurentUserHome extends EntityHome<User> {
   
   @Out(scope=ScopeType.SESSION, required=false)
   @In(required=false)
   private Integer currentUserId;

   @Factory(value="currentUser")
   public User getCurrentUser() {
      return getInstance();       
   }
        
   @Override
   protected User createInstance() {
      if (currentUserId != null) {
         return getEntityManager().find(User.class, currentUserId);
      } else {
         return new User();
      }
   }

}

Then, to set the current user, just outject currentUserId into SESSION scope.

How does it work?

Well, if you simply annotate your entity with @Name and @Scope, then, when loaded (using a SMPC) and outjected, it is stored in the desired context. BUT, if you then leave the conversation in which it was loaded, you will get an LIE if you access a property which was not initialised inside that conversation. Ouch.

So, instead of storing the entity in a scope longer than Conversation, we store the id of the entity in the long running scope and the entity in the conversation. Using a @Factory we initialise the entity context variable. If the id is null a new entity is created, otherwise a fresh copy of the entity is loaded and attached to this conversation.

7 comments:
 
16. Jan 2008, 16:10 America/New_York | Link

I really like this approach better than an example I had provided and I think I'll use it :) It's really nice not to have to carry the currentUser (and potentially its associations) in memory unless absolutely needed by the current context. Thanks Pete.

 
11. Apr 2008, 19:11 America/New_York | Link

You can also use the hibernate cache so your objects are always attached in a conversation and no database calls are made!

 
12. Apr 2008, 03:00 America/New_York | Link

This is really helpful! Thank you.

@Tom: can you elaborate or provide a link? Thanks!

 

--

Devon Hillard

Tech Blog

 
15. Dec 2008, 10:59 America/New_York | Link
Tobias

The example with currentUser above works fine for all cases except when a restrict-annotations leads us via the login-screen and back. When this happens, the setting of the currentUserId in the session seems to be ok, but the lookup fetching the currentUser does not happen on return to the method where the the restrict sent us to the login- method/page in the first place. It resolves to null no matter what. In the next reload of the page the current user is fetched as normal though.

I have not found any non-ugly workaround for this problem ... why I think that the pattern above is somewhat flawed for this particular case.

 
09. Jan 2009, 15:13 America/New_York | Link

Hi Pete - this is a really useful technique which I plan to implement in my app. However, I have a question for anyone who has implemented this already - if I want to be able to work with the currentUser and another user instance (e.g. perhaps my currentUser is an administrator who can also create new user accounts) do I need to create CurrentUserHome and UserHome separately or can I just create a UserHome with separate @factory methods for user and currentUser?

 
18. Mar 2009, 11:49 America/New_York | Link

In our projects we heavily depend on jBPM carrying variables in BUSINESS_PROCESS context. The variables lives are managed by a trivial subclass of org.jbpm.context.exe.Converter, converting id<->Entity (it supports only @Entity classes with Long or String @Id).

Pete, does your approach have any performance advantages over using a jBPM variable converter?

 
09. Apr 2009, 09:04 America/New_York | Link

Maybe its just me but it took me a few minutes to figure out how I use this approach without having the currentUserId used in a page parameter, which is the main thing I was trying to avoid otherwise you can simply change the currentUserId and get into someone else's profile. So in the AuthenticationManager I @Out-jected the currentUserId there and now it works just like I wanted.