У меня есть веб-приложение на основе JSF 2 @ViewScoped
, и я не могу заставить транзакции работать правильно, или, скорее: они вообще не запускаются.
Я также использую CDI и EJB3 Java EE 6.
Вот основная фасоль:
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;
}
...
}
Компонент с областью представления используется на странице JSF для отображения простого списка в таблице данных. Он был сделан с областью просмотра, потому что в нем есть операции на основе AJAX для добавления элементов, удаления элементов и их сортировки с помощью RichFaces (фильтрация).
Я добавил @Stateless
для каждого вызова метода, чтобы начать транзакцию (или создать новую, если таковой не существует, по умолчанию TransactionAttributeType.REQUIRED
). Эта идея была взята из книги Core JavaServer Faces, 3-е изд., однако я не нашел ни одного примера, который соответствовал бы моему собственному.
Внедренный класс PqService (вместо него не имеет значения использование @EJB
):
@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 (в основном взято из примера Адама Биена 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;
}
...
}
Единственная разница в том, что я подкласс JpaCrudService
, потому что мне не нравятся запросы, хранящиеся в/в объектах. Поэтому я пропустил аннотацию @Local
(поправьте меня, если это неправильно). @Stateless
не унаследован AFAIK, и я только ввожу подклассы, поэтому я также прокомментировал это.
При этом доступ к bean-компоненту осуществляется со страницы JSF:
<rich:dataTable value="#{pqManager.pqItems}"
var="pq">
<f:facet name="header">
<h:outputText value="Active" />
</f:facet>
...
Однако при загрузке страницы я получаю исключение:
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)
.
.
.
Это не удается, потому что вызов pqService.findActivePqsFor()
не выполняется в существующей транзакции (TransactionAttributeType.MANDATORY
, которая унаследована, насколько я знаю).
Обратите внимание, что страница отображается правильно без использования транзакций путем удаления TransactionAttributeType.MANDATORY
на JpaCrudService
и использования расширенного менеджера сущностей, но это было только для целей тестирования.
Но почему это не работает? Почему транзакция не начинается здесь? Есть ли что-нибудь с bean-компонентом JSF @ViewScoped
? Несовместимо?
Как это исправить?
PS: я использую JBoss AS 7.1.1.