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.
| Online: | 8 Members of 8202 |
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.
@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.
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.
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.
You can also use the hibernate cache so your objects are always attached in a conversation and no database calls are made!
This is really helpful! Thank you.
@Tom: can you elaborate or provide a link? Thanks!
--
Devon Hillard
Tech Blog
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 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.
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 and ?
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?
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.