TransactionRequiredException with em as producer field
janey Feb 12, 2010 11:41 AMHey all,
I was happy to hear that the bug -- WELD attemps to close the EntityManager Link -- has been fixed with WELD-1.0.1.CR2. So I downloaded the new version and tried again the example from WELD using a producer field for injecting the EM:
@SessionScoped @Named("login") @Stateful public class LoginAction implements Serializable, ILoginActionLocal { private static final long serialVersionUID = 1L; private User user; @Inject Credentials credentials; /** * inject EM */ @Inject @BankDatabase EntityManager database; // --> only works once //@PersistenceContext private EntityManager database; // --> always works fine /** * */ public void login() { // logout old user logout(); user = (User) database.find(User.class, credentials.getUsername()); if(user == null) { FacesContext.getCurrentInstance().addMessage(null, new FacesMessage( "User does not exist: " + credentials.getUsername())); return; } FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Welcome, " + credentials.getUsername())); } /** * */ public void register() { User user = (User) database.find(User.class, credentials.getUsername()); if(user != null) { FacesContext.getCurrentInstance().addMessage(null, new FacesMessage( "User already registered: " + user.getName() + ", at date: "+ user.getInsertDate())); } else { user = new User(); user.setName(credentials.getUsername()); user.setInsertDate(new Date()); user.setPassword(credentials.getPassword()); database.persist(user); database.flush(); FacesContext.getCurrentInstance().addMessage(null, new FacesMessage( "Welcome, " + credentials.getUsername())); } } /** * */ public void logout() { user = null; } [...] }
Database producer class:
public class BankDatabaseProducer { @Produces @BankDatabase @PersistenceContext EntityManager userDatabase; }
The login form:
<ui:composition template="template.xhtml"> <ui:define name="content"> <h:messages/> <h:form> <h:panelGrid columns="2" rendered="#{!login.loggedIn}"> <h:outputLabel for="username">Username:</h:outputLabel> <h:inputText id="username" value="#{credentials.username}"/> <h:outputLabel for="password">Password:</h:outputLabel> <h:inputText id="password" value="#{credentials.password}"/> </h:panelGrid> <h:commandButton value="Login" action="#{login.login}" rendered="#{!login.loggedIn}"/> <h:commandButton value="Register" action="#{login.register}" rendered="#{!login.loggedIn}"/> <h:commandButton value="Logout" action="#{login.logout}" rendered="#{login.loggedIn}"/> </h:form> </ui:define> </ui:composition> </html>
If I use the EM injected by the @BankDatabase producer field, I am getting following error, when calling the register method twice (!) The first call is successfull.
11:30:20,455 ERROR[javax.enterprise.resource.webcontainer.jsf.application] javax.ejb.EJBException: javax.persistence.TransactionRequiredException: no transaction is in progress: javax.faces.el.EvaluationException: javax.ejb.EJBException: javax.persistence.TransactionRequiredException: no transaction is in progress at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:102) at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:102) at javax.faces.component.UICommand.broadcast(UICommand.java:315) at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:775) at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1267) at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:82) at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
Using @PersistenceContext private EntityManager database;
always works fine.
Why is that? What is wrong with my code? I googled a lot and always found, that the EntityManager should care for all transactions. I also tried to use the @SessionScope annotation with the producer field, because I thought it might be destroyed to early (producer fields are scoped dependend by default). But nothing changed.
Can you give me a hint?