-
1. Re: How to avoid LazyInitializationException
pmuir Jul 9, 2008 6:13 PM (in response to johnnyren) -
2. Re: How to avoid LazyInitializationException
johnnyren Jul 10, 2008 5:12 AM (in response to johnnyren)in the book "JBOSS SEAM", Michael Yuan wrote in chapter 7, section 7.1 " in addition, we gave EntityMnager and EXTENDED type, which allows it to lazy load more data from the database as needed ...".
Did I miss anything?
// begin
@Stateful
@Name("manager")
@Scope (SESSION)
public class ManagerAction implements Manager {
@In @Out
private Person person;
private List <Person> fans;
private boolean confirmed = false;
@PersistenceContext
private EntityManager em;
public String sayHello () {
System.out.println("sayHello called");
if (person.getName()
.matches("^[a-zA-Z.-]+ [a-zA-Z.-]+")
|| confirmed) {
em.persist (person);
confirmed = false;
find ();
return "fans";
} else {
// confirmed = true;
return "warning";
}
}
-
3. Re: How to avoid LazyInitializationException
admin.admin.email.tld Jul 10, 2008 6:44 AM (in response to johnnyren)nice to hear your voice, Pete. I viewed the web beans Gaving King lecture from Australia today as well. Interesting annotations...
-
4. Re: How to avoid LazyInitializationException
admin.admin.email.tld Jul 10, 2008 7:15 AM (in response to johnnyren)
johnny ren wrote on Jul 10, 2008 05:12:
in the bookJBOSS SEAM
, Michael Yuan wrote in chapter 7, section 7.1in addition, we gave EntityMnager and EXTENDED type, which allows it to lazy load more data from the database as needed ...
.
Did I miss anything?// begin @Stateful @Name("manager") @Scope (SESSION) public class ManagerAction implements Manager { @In @Out private Person person; private List <Person> fans; private boolean confirmed = false; @PersistenceContext private EntityManager em; public String sayHello () { System.out.println("sayHello called"); if (person.getName() .matches("^[a-zA-Z.-]+ [a-zA-Z.-]+") || confirmed) { em.persist (person); confirmed = false; find (); return "fans"; } else { // confirmed = true; return "warning"; } }
With Seam apps, you should use @In EntityManager (SMPC) as much as possible. Here is a quote from the Seam ref doc:
Hibernate users developed the open session in view pattern to work around this problem. In the Hibernate
community, open session in view was historically even more important because frameworks like Spring use
transaction-scoped persistence contexts. So rendering the view would cause LazyInitializationExceptions
when unfetched associations were accessed.
This pattern is usually implemented as a single transaction which spans the entire request. There are several
problems with this implementation, the most serious being that we can never be sure that a transaction is successful
until we commit it—but by the time the open session in view transaction is committed, the view is
fully rendered, and the rendered response may already have been flushed to the client. How can we notify the
user that their transaction was unsuccessful?
Seam solves both the transaction isolation problem and the association fetching problem, while working around
the problems with open session in view. The solution comes in two parts:
use an extended persistence context that is scoped to the conversation, instead of to the transaction
use two transactions per request; the first spans the beginning of the update model values phase until the
end of the invoke application phase; the second spans the render response phase.The example code above is using transaction-scoped PersistenceContext (which is the default). See section 8.4.1 of JSR220-persistence.
One key difference b/n using @PersistenceContext vs. @In (SMPC) to inject the EntityManager instance is that SMPC introduces flushModeType MANUAL which is basically an extension of JSR220 (the standard options are AUTO or COMMIT).
Using MANUAL allows you to flush the persistence context manually when you end the conversation (i.e. in a @End method). This will prevent premature CRUD operations prior to the ending of a conversation.
There are other advantages to using SMPC EntityManager that have to do with clustering:
Seam-managed persistence contexts are extremely efficient in a clustered environment. Seam is able to perform
an optimization that EJB 3.0 specification does not allow containers to use for container-managed extended
persistence contexts. Seam supports transparent failover of extended persisence contexts, without the need to
replicate any persistence context state between nodes. (We hope to fix this oversight in the next revision of the
EJB spec.)Here's a key point regarding when/why to use SMPC:
If you're using Seam outside of a Java EE 5 environment, you can't rely upon the container to manage the persistence context lifecycle for you. Even if you are in an EE 5 environment, you might have a complex application with many loosly coupled components that collaborate together in the scope of a single conversation, and in this case you might find that propagation of the persistence context between component is tricky and error-prone. In either case, you'll need to use a managed persistence context (for JPA) or a managed session (for Hibernate) in your components. A Seam-managed persistence context is just a built-in Seam component that manages an instance of EntityManager or Session in the conversation context. You can inject it with @In. -
5. Re: How to avoid LazyInitializationException
dan.j.allen Jul 10, 2008 7:16 AM (in response to johnnyren)Actually, this is not true. You can use the extended persistence context (PersistenceContextType.EXTENDED) on a SFSB to avoid lazy initialization exceptions in both the view and any subsequent request in the same conversation.
In a Seam application, a SFSB is stored in the conversation (by default). Since an extended persistence context attaches to the SFSB for its lifetime, the extended persistence context lives in the conversation transitively.
That raises the question,
How does the extended persistence context from Java EE and the Seam-managed persistence context differ, then?
You'll be happy to know that this is covered in great detail in chapter 8/9 of Seam in Action. I spent time with it because it is an important question. (There is also great coverage in chapter 11 of Java Persistence w/ Hibernate)Here are the downsides of using a SFSB to manage an extended persistence context:
- The persistence context is bound to a single component (the SFSB) and is closed when that component is removed
- The rules for propagating an extended persistence context between SFSB are complex, stupid, and arbitrary
- The persistence context is not easily accessed from JavaBean components (you have to basically hack it)
- You don't get manual flushing of the persistence context controlled by Seam
Basically, the extended persistence context in Java EE is a nice first try, but Seam does it sooooo much better. So unless you enjoy using pure Java EE resources for the fun of it, use the Seam-managed persistence context.
-
6. Re: How to avoid LazyInitializationException
dan.j.allen Jul 10, 2008 7:53 AM (in response to johnnyren)Because I am a man of my code, I have decided to back this up with something you can test. Create a seam-gen EAR app. Now add the following
User.java
@Entity public class User implements Serializable { private Long id; private String name; private Set<Email> emails; @Id @GeneratedValue public Long getId() { return id; } public void setId(Long id) { this.id = id; } @Length(max = 20) public String getName() { return name; } public void setName(String name) { this.name = name; } @OneToMany(fetch = FetchType.LAZY, mappedBy = "user") public Set<Email> getEmails() { return emails; } public void setEmails(Set<Email> emails) { this.emails = emails; } }
Email.java
@Entity public class Email implements Serializable { private Long id; private String name; @Id @GeneratedValue public Long getId() { return id; } public void setId(Long id) { this.id = id; } @Length(max = 20) public String getName() { return name; } public void setName(String name) { this.name = name; } }
UserList.java
@Local public interface UserList { public List<User> findUsers(); public void showEmails(boolean state); public Object getDelegate(); public void remove(); }
UserListBean.java
@Stateful @Name("userList") public class UserListBean implements UserList { @PersistenceContext(type = PersistenceContextType.EXTENDED) private EntityManager em; @Out(scope = ScopeType.EVENT) boolean showEmails = false; @Factory("users") @Begin(flushMode = FlushModeType.MANUAL) public List<User> findUsers() { return em.createQuery("select u from User u") .getResultList(); } public void showEmails(boolean state) { showEmails = state; } public Object getDelegate() { return em.getDelegate(); } @Remove public void remove() {} }
users.xhtml
<p>Flush mode is #{userList.delegate.flushMode} not MANUAL</p> <rich:dataTable var="_user" value="#{users}"> <rich:column> <f:facet name="header">Object Signature</f:facet> #{_user} </rich:column> <rich:column> <f:facet name="header">Name</f:facet> #{_user.name} </rich:column> <rich:column> <f:facet name="header">Emails (lazy loaded)</f:facet> <rich:dataList var="_email" value="#{_user.emails.{e|e}}" rendered="#{showEmails}"> #{_email.name} </rich:dataList> <h:outputText value="Emails hidden" rendered="#{not showEmails}"/> </rich:column> </rich:dataTable> <s:link action="#{userList.showEmails(true)}" view="/users.xhtml" rendered="#{not showEmails}" value="Show emails"/> <s:link action="#{userList.showEmails(false)}" view="/users.xhtml" rendered="#{showEmails}" value="Hide emails"/>
When the page renders the first time, a long-running conversation is started when the users are retrieved. The email addresses will only be shown when the link is clicked to show them. That proves they are loading super lazily.
The message above the table proves that Seam was not able to switch the EntityManager to manual flush mode, even if the provider is Hibernate.
Enjoy!
-
7. Re: How to avoid LazyInitializationException
dan.j.allen Jul 10, 2008 7:55 AM (in response to johnnyren)I happen to think my four points are simpler than what the Seam reference documentation says. Frankly, for most people, the clustering argument is lost on them. Besides, until I see some numbers, I'm not going to believe it anyway (I don't just take people's word on these types of things).
-
8. Re: How to avoid LazyInitializationException
pmuir Jul 10, 2008 12:03 PM (in response to johnnyren)
Dan Allen wrote on Jul 10, 2008 07:16:- The persistence context is bound to a single component (the SFSB) and is closed when that component is removed
- The rules for propagating an extended persistence context between SFSB are complex, stupid, and arbitrary
- The persistence context is not easily accessed from JavaBean components (you have to basically hack it)
- You don't get manual flushing of the persistence context controlled by Seam
- The XPC isn't available during the render phase
I happen to think my four points are simpler than what the Seam reference documentation says.So fix it :p
-
9. Re: How to avoid LazyInitializationException
dan.j.allen Jul 10, 2008 6:27 PM (in response to johnnyren)
So fix it :pAbsolutely. I will be contributing back to the Seam reference documentation just as soon as my book is done at the end of this month ;) I didn't mean to sound so high and mighty ;)
The XPC isn't available during the render phaseI'm willing to say that we are somehow thinking about two different things, but it's definitely available in the render phase. I think my example proves that.
The persistence context is just a glorified hash map. The fact that the hash map is bound to a SFSB which is stored in the conversation means that the XPC has to be available anywhere the conversation lives. And since an EntityManager can open a database connection whenever it needs one, then by those two points, it is available and can perform additional lookups in the render phase.
-
10. Re: How to avoid LazyInitializationException
pmuir Jul 11, 2008 2:47 PM (in response to johnnyren)Yes, you are right, this is due to SMT, not SMPC
-
11. Re: How to avoid LazyInitializationException
johnnyren Jul 11, 2008 3:35 PM (in response to johnnyren)Thanks all of you for helping. Special thanks to Dan for providing code example.
-
12. Re: How to avoid LazyInitializationException
stockinstore Aug 7, 2009 12:37 PM (in response to johnnyren)if use @In EntityManager not use @PersistenceContext
how to set (PersistenceContextType.EXTENDED) -
13. Re: How to avoid LazyInitializationException
dan.j.allen Aug 7, 2009 7:48 PM (in response to johnnyren)The extended persistence context functionality is implicit for a Seam-managed (or Seam-injected) EntityManager. The short answer, you get it for free.