2015-06-23 12 views
9

Avendo seguito, il codice abbastanza semplice e configurato correttamente contesto di persistenza JTA-based:@Transactional ignorato nella classe di base del CDI Bean

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> { 

} 

sto incontrando seguente eccezione durante il richiamo 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) 

In Per risolvere il problema devo aggiungere:

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

Wh a potrebbe causare tale eccezione? L'annotazione @Transactional è contrassegnata come @Inherited.

+0

[I CDI non supportano le transazioni, tranne che si utilizza Java EE 7] (http://stackoverflow.com/questions/17838221/jee7-do-ejb-and-cdi-beans-support-container-managed-transactions) –

+0

Sono in esecuzione su Glassfish4 e JEE7 quindi dovrebbe essere supportato (JTA 1.2), e quando dichiaro '@ Transactional' nella classe concentrato ** è ** funzionante. – Crozin

+0

Puoi condividere il tuo 'beans.xml'? –

risposta

5

Questo perché il contenitore non vedrà annotazioni nei metodi locali: in AbstractRepository il metodo è annotato ma è locale rispetto a MyEntityRepository. Questo rende invisibile a Glassfish che non avvierà alcuna transazione, quindi la tua eccezione.

QuestionnaireRepository deve immettere MyEntityRepository e deve iniziare la transazione stessa.

La conclusione da portare a casa è: @Transactional le annotazioni vengono rilevate solo sui metodi business, che sono metodi pubblici chiamati dalla classe 'injector'.

Vedi anche an example in which the method is in the same instance

Stavo pensando a come si potrebbe fare la stessa cosa senza implementare persist nella classe concreta, ma non credo che sia possibile, perché il contenitore dovrebbe istanziare una classe astratta (che è assurdo in quanto non hanno costruttore da chiamare tramite riflessione).

Forse potresti fare qualcosa con Java 8 e i metodi di interfaccia predefiniti, che vengono aggiunti come comportamento predefinito in una classe concreta.

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

Ho intenzione di avere oltre una dozzina di tali repository con un'implementazione quasi identica (per diversi metodi). Creare una serie di metodi proxy in ognuno di essi è possibile ma sembra un modo sbagliato e violazione di DRY. – Crozin

+1

@Crozin Perché sì, dovresti evitare una simile struttura. I repository dovrebbero essere usati solo per aggregati significativi.Ci dovrebbe essere una manciata di aggregati per sistema e molto probabilmente hanno un'implementazione completamente diversa, quindi non c'è bisogno di usare una classe astratta. – gurghet

+3

Inoltre, la transazione deve essere avviata a livello di applicazione, non a livello di infrastruttura. Quindi non dovresti usare @Transactional nei repository – gurghet

1

Grazie per aver aggiunto il file beans.xml. Se dovessi indovinare, GF4 soffre di un bug di CDI 1.1 in cui gli intercettatori non definiscono i bean e nella tua app viene utilizzata la modalità di rilevamento errata. Se si modifica il file beans.xml utilizzare

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

Questo costringerà a trovare tutti i fagioli. Il problema è che la tua classe base non viene scoperta e di conseguenza non sta avendo lo stato della transazione applicato al metodo.

+0

Ho cambiato 'beans.xml' ma non cambia nulla, sto ancora ricevendo' javax.persistence.TransactionRequiredException'. Potrei passare a EJB ma volevo provare a CDI. – Crozin

Problemi correlati