3 Replies Latest reply on Feb 17, 2010 4:35 PM by janey

    TransactionRequiredException with em as producer field

    janey

      Hey 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?