2015-06-23 2 views
9

Имея следующий, довольно простой код и правильно настроенное контекст JTA на основе постоянства:@Transactional игнорируется в базовом классе CDI Бина

abstract class AbstractRepository<E> { 
    @PersistenceContext 
    protected EntityManager em; 

    @Transactional 
    public synchronized void persist(E entity) { 
     em.persist(entity); 
     em.flush(); 
    } 
} 

@ApplicationScoped 
class MyEntityRepository extends AbstractRepository<MyEntity> { 

} 

Я встречая следующее исключение при вызове MyEntityRepository.persist():

2015-06-23T12:34:55.233+0200|Severe: javax.persistence.TransactionRequiredException 
    at com.sun.enterprise.container.common.impl.EntityManagerWrapper.doTxRequiredCheck(EntityManagerWrapper.java:161) 
    at com.sun.enterprise.container.common.impl.EntityManagerWrapper.doTransactionScopedTxCheck(EntityManagerWrapper.java:151) 
    at com.sun.enterprise.container.common.impl.EntityManagerWrapper.persist(EntityManagerWrapper.java:281) 
    at my.project.AbstractRepository.persist(AbstractRepository.java:28) 
    at my.project.QuestionnaireRepository.persist(QuestionnaireRepository.java:1) 
    at my.project.QuestionnaireRepository$Proxy$_$$_WeldClientProxy.persist(Unknown Source) 
    at my.project.QuestionnaireForm.save(QuestionnaireForm.java:29) 
    at my.project.QuestionnaireForm.lambda$0(QuestionnaireForm.java:1) 
    at my.project.QuestionnaireForm$$Lambda$56/1079229220.buttonClick(Unknown Source) 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
    at java.lang.reflect.Method.invoke(Method.java:483) 
    at com.vaadin.event.ListenerMethod.receiveEvent(ListenerMethod.java:508) 
    at com.vaadin.event.EventRouter.fireEvent(EventRouter.java:198) 
    at com.vaadin.event.EventRouter.fireEvent(EventRouter.java:161) 
    at com.vaadin.server.AbstractClientConnector.fireEvent(AbstractClientConnector.java:977) 
    at com.vaadin.ui.Button.fireClick(Button.java:393) 
    at com.vaadin.ui.Button$1.click(Button.java:61) 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
    at java.lang.reflect.Method.invoke(Method.java:483) 
    at com.vaadin.server.ServerRpcManager.applyInvocation(ServerRpcManager.java:168) 
    at com.vaadin.server.ServerRpcManager.applyInvocation(ServerRpcManager.java:118) 
    at com.vaadin.server.communication.ServerRpcHandler.handleInvocations(ServerRpcHandler.java:291) 
    at com.vaadin.server.communication.ServerRpcHandler.handleRpc(ServerRpcHandler.java:184) 
    at com.vaadin.server.communication.UidlRequestHandler.synchronizedHandleRequest(UidlRequestHandler.java:92) 
    at com.vaadin.server.SynchronizedRequestHandler.handleRequest(SynchronizedRequestHandler.java:41) 
    at com.vaadin.server.VaadinService.handleRequest(VaadinService.java:1408) 
    at com.vaadin.server.VaadinServlet.service(VaadinServlet.java:350) 
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:790) 
    at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1682) 
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:344) 
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:214) 
    at org.glassfish.tyrus.servlet.TyrusServletFilter.doFilter(TyrusServletFilter.java:295) 
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256) 
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:214) 
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:316) 
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:160) 
    at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:734) 
    at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:673) 
    at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:99) 
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:174) 
    at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:415) 
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:282) 
    at com.sun.enterprise.v3.services.impl.ContainerMapper$HttpHandlerCallable.call(ContainerMapper.java:459) 
    at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:167) 
    at org.glassfish.grizzly.http.server.HttpHandler.runService(HttpHandler.java:201) 
    at org.glassfish.grizzly.http.server.HttpHandler.doHandle(HttpHandler.java:175) 
    at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:235) 
    at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:119) 
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:284) 
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:201) 
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:133) 
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:112) 
    at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77) 
    at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:561) 
    at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:112) 
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:117) 
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:56) 
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:137) 
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:565) 
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:545) 
    at java.lang.Thread.run(Thread.java:745) 

В для его исправления Я должен добавить:

@Override 
@Transactional 
public void persist(Entity e) { 
    super.persist(e); 
} 

Wh может вызвать такое исключение? @Transactional аннотация обозначена как @Inherited.

+0

[CDI не поддерживают транзакции, за исключением использования Java EE 7] (http://stackoverflow.com/questions/17838221/jee7-do-ejb-and-cdi-beans-support-container-managed-transactions) –

+0

Я бегу на Glassfish4 и JEE7, поэтому он должен поддерживаться (JTA 1.2), и когда я объявляю '@ Transactional' в классе концентрата **, он работает **. – Crozin

+0

Можете ли вы поделиться своим «beans.xml»? –

ответ

5

Это потому, что контейнер не будет видеть аннотацию в локальных методах: в AbstractRepository метод аннотируется, но является локальным относительно MyEntityRepository. Это делает его невидимым для Glassfish, который не инициирует никаких транзакций, следовательно, ваше исключение.

QuestionnaireRepository должен вводить MyEntityRepository и должен инициировать транзакцию.

Вывод для дома: @Transactional аннотации принимаются только по бизнес-методам, которые являются общедоступными методами, называемыми классом «инжектор».

Смотрите также an example in which the method is in the same instance

Я думал о том, как вы могли бы сделать то же самое, не реализуя persist в конкретном классе, но я не думаю, что это возможно, потому что контейнер должен был бы создать экземпляр абстрактного класса (который нелепый, поскольку у них нет конструктора для вызова через отражение).

Возможно, вы могли бы что-то сделать с помощью Java 8 и методов интерфейса по умолчанию, которые добавляются как поведение по умолчанию в конкретном классе.

// UNTESTED! 
public interface Repository<E> { 
    @Transactional 
    synchronized default void persist(E entity) { 
     em.persist(entity); 
    } 
} 
+0

Я собираюсь имеют более десятка таких репозиториев с почти одинаковой реализацией (для нескольких методов). Создание пучка прокси-методов в каждом из них возможно, но это похоже на неправильный способ и нарушение DRY. – Crozin

+1

@Crozin Почему да, вы должны избегать такой структуры. Хранилища должны использоваться только для значимых агрегатов.В каждой системе должно быть несколько агрегатов, и они, скорее всего, имеют совершенно другую реализацию, поэтому нет необходимости использовать абстрактный класс. – gurghet

+3

Кроме того, транзакция должна быть инициирована на уровне приложения, а не на уровне инфраструктуры. Поэтому вы не должны использовать @Transactional вообще в репозиториях. – gurghet

1

Благодарим за добавление файла beans.xml. Если я должен был догадаться, GF4 страдает от ошибки CDI 1.1, где перехватчики не являются компонентами, а неправильный режим обнаружения используется в вашем приложении. Если вы измените файл beans.xml использовать

<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee" 
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
     http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd" 
     bean-discovery-mode="all"> 
</beans> 

Это заставит его найти все бобы. Проблема заключается в том, что ваш базовый класс не обнаружен и в результате не применяется состояние транзакции к методу.

+0

Я изменил 'beans.xml', но ничего не меняет, я все равно получаю' javax.persistence.TransactionRequiredException'. Я мог бы переключиться на EJB, но я хотел попробовать CDI. – Crozin

Смежные вопросы