Domanda

I have a JSF 2 @ViewScoped based webapp, that I cannot get the transactions to go with correctly, or rather: they don't start at all.

I'm using Java EE 6's CDI and EJB3, too.

Here's the main bean:

import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.inject.Inject;
...

@ManagedBean
@ViewScoped
@Stateless
public class PqManager implements Serializable
{
    private List<PqListItem> pqItems;

    @Inject
    private PqService pqService;

    public List<PqListItem> getPqItems()
    {
        if ( pqItems == null )
        {
            pqItems = pqService.findActivePqs();
        }

        return pqItems;
    }

    ...
}

The view-scoped bean is used from a JSF page to display a simple list in a datatable. It was made view-scoped because it has AJAX-based operations to add items, remove items, and to sort them via RichFaces (filtering).

I added @Stateless for every method invocation to start a transaction (or create a new one if none exists, the default is TransactionAttributeType.REQUIRED). This idea was taken from the book 'Core JavaServer Faces, 3rd ed.', however I haven't found any examples that would match my own.

Injected PqService class (it doesn't make a difference to use @EJB instead):

@Stateless
public class PqService extends JpaCrudService
{
    ...

    public List<PqListItem> findActivePqs()
    {
        return em.createQuery("SELECT NEW ... whatever not interesting here... WHERE pq.workflow = '" + Workflow.ACTIVE + "' GROUP BY pq.id", PqListItem.class).getResultList();
    }

    ...
}

JpaCrudService (basically taken from Adam Bien's example http://www.adam-bien.com/roller/abien/entry/generic_crud_service_aka_dao):

//@Stateless
//@Local(CrudService.class)
@TransactionAttribute(TransactionAttributeType.MANDATORY)
public abstract class JpaCrudService implements CrudService
{
    @PersistenceContext(unitName = "PqGeneratorPu")
    protected EntityManager em;

    @Override
    public <T> T create(T t)
    {
        em.persist(t);
        em.flush();
        em.refresh(t);

        return t;
    }

    ...
}

The only difference is that I subclass JpaCrudService because I don't like queries stored in/at the entities. So I omitted the @Local annotation (correct me if that's wrong). @Stateless isn't inherited AFAIK and I only inject the subclasses so I also commented that one out.

That said, the bean is then accessed from a JSF page:

  <rich:dataTable value="#{pqManager.pqItems}"
                  var="pq">
    <f:facet name="header">
      <h:outputText value="Active" />
    </f:facet>
    ...

However, when loading the page, I get an exception:

javax.ejb.EJBTransactionRequiredException: Transaction is required for invocation: org.jboss.invocation.InterceptorContext@7a6c1c92
    at org.jboss.as.ejb3.tx.CMTTxInterceptor.mandatory(CMTTxInterceptor.java:255)
    at org.jboss.as.ejb3.tx.CMTTxInterceptor.processInvocation(CMTTxInterceptor.java:184)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288)
    at org.jboss.as.ejb3.component.interceptors.CurrentInvocationContextInterceptor.processInvocation(CurrentInvocationContextInterceptor.java:41)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288)
    at org.jboss.as.ejb3.component.interceptors.LoggingInterceptor.processInvocation(LoggingInterceptor.java:59)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288)
    at org.jboss.as.ee.component.NamespaceContextInterceptor.processInvocation(NamespaceContextInterceptor.java:50)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288)
    at org.jboss.as.ee.component.TCCLInterceptor.processInvocation(TCCLInterceptor.java:45)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288)
    at org.jboss.invocation.ChainedInterceptor.processInvocation(ChainedInterceptor.java:61)
    at org.jboss.as.ee.component.ViewService$View.invoke(ViewService.java:165)
    at org.jboss.as.ee.component.ViewDescription$1.processInvocation(ViewDescription.java:173)
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288)
    at org.jboss.invocation.ChainedInterceptor.processInvocation(ChainedInterceptor.java:61)
    at org.jboss.as.ee.component.ProxyInvocationHandler.invoke(ProxyInvocationHandler.java:72)
    at de.company.webapp.service.PqService$$$view95.findActivePqsFor(Unknown Source)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.jboss.weld.util.reflection.SecureReflections$13.work(SecureReflections.java:264)
    at org.jboss.weld.util.reflection.SecureReflectionAccess.run(SecureReflectionAccess.java:52)
    at org.jboss.weld.util.reflection.SecureReflectionAccess.runAsInvocation(SecureReflectionAccess.java:137)
    at org.jboss.weld.util.reflection.SecureReflections.invoke(SecureReflections.java:260)
    at org.jboss.weld.bean.proxy.EnterpriseBeanProxyMethodHandler.invoke(EnterpriseBeanProxyMethodHandler.java:111)
    at org.jboss.weld.bean.proxy.EnterpriseTargetBeanInstance.invoke(EnterpriseTargetBeanInstance.java:56)
    at org.jboss.weld.bean.proxy.ProxyMethodHandler.invoke(ProxyMethodHandler.java:105)
    at de.company.webapp.service.PqService$Proxy$_$$_Weld$Proxy$.findActivePqs(PqService$Proxy$_$$_Weld$Proxy$.java)
    at de.company.webapp.facade.PqManager.getPqItems(PqManager.java:84)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    .
    .
    .

It fails because the call pqService.findActivePqsFor() doesn't run in an existing transaction (TransactionAttributeType.MANDATORY, which is inherited AFAIK).

Note, that the page is displayed correctly without using transactions by deleting the TransactionAttributeType.MANDATORY on the JpaCrudService and using an extended entity manager, but this was just for testing purposes.

But why isn't this working? Why isn't the transaction started here? Is there anything with the JSF @ViewScoped bean? Incompatible?

How do you repair this?

PS: I'm using JBoss AS 7.1.1.

È stato utile?

Soluzione

Of you're using CDI drop the JSF annotations. JSF annotations don't control EJBs the way CDI will. You're probably confusing the container with the annotations being used. You could also use MyFaces CODI for some extensions or look at recreating the ViewScope with CDI. There are a few examples online.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top