2015-06-23 7 views
9

folgenden Eigenschaften, recht einfachen Code und richtig konfiguriert JTA-basierte Persistenzkontext:@Transactional in CDI Bean Basisklasse ignoriert

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

} 

ich folgende Ausnahme bin Begegnung während MyEntityRepository.persist() Aufruf:

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 Um es zu beheben, muss ich hinzufügen:

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

Wh könnte eine solche Ausnahme verursachen? @Transactional Anmerkung wird als @Inherited markiert. Diese

+0

[CDIs unterstützen keine Transaktionen, außer Sie verwenden Java EE 7] (http://stackoverflow.com/questions/17838221/jee7-do-ejb-and-cdi-beans-support-container-managed-transactions) –

+0

Ich laufe auf Glassfish4 und JEE7, so dass es unterstützt werden sollte (JTA 1.2), und wenn ich '@ Transactional' in der Konzentratklasse ** deklariere, funktioniert es **. – Crozin

+0

Können Sie Ihre 'beans.xml' teilen? –

Antwort

5

ist, weil der Behälter nicht Annotation in lokalen Methoden sehen: in AbstractRepository das Verfahren erwähnt wird, aber lokal relativ zu MyEntityRepository. Dies macht es für Glassfish unsichtbar, das keine Transaktion initiiert, daher Ihre Ausnahme.

QuestionnaireRepository sollte MyEntityRepository injizieren und sollte die Transaktion selbst initiieren.

Die Schlussfolgerung zu Hause zu bringen ist: @Transactional Annotationen werden nur auf Geschäftsmethoden, die öffentliche Methoden von der "Injektor" -Klasse genannt werden abgeholt.

Siehe auch an example in which the method is in the same instance

Ich habe darüber nachgedacht, wie Sie das gleiche tun könnten persist in der konkreten Klasse ohne Umsetzung, aber ich glaube nicht, dass es möglich ist, weil der Behälter eine abstrakte Klasse instanziiert würde (was absurd, da sie keinen Konstrukteur haben, der über Reflexion aufrufen kann.

Vielleicht könnten Sie etwas mit Java 8 und Interface-Standard-Methoden tun, die als Standardverhalten in einer konkreten Klasse hinzugefügt werden.

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

ich werde haben über ein Dutzend solcher Repositories mit fast identischer Implementierung (für mehrere Methoden). Es ist möglich, eine Reihe von Proxy-Methoden in jedem von ihnen zu erstellen, aber es scheint ein falscher Weg und eine Verletzung von DRY zu sein. – Crozin

+1

@Crozin Warum sollten Sie eine solche Struktur vermeiden? Repositories sollten nur für sinnvolle Aggregate verwendet werden.Es sollte eine Handvoll Aggregate pro System geben, und sie haben höchstwahrscheinlich eine ziemlich unterschiedliche Implementierung, so dass keine abstrakte Klasse verwendet werden muss. – gurghet

+3

Darüber hinaus sollte die Transaktion auf Anwendungsebene und nicht auf Infrastrukturebene initiiert werden. Also sollten Sie @Transactional überhaupt nicht in Repositories verwenden. – gurghet

1

Vielen Dank für das Hinzufügen Ihrer beans.xml Datei. Wenn ich raten müsste, leidet GF4 von einem CDI 1.1 Bug, bei dem Abfangjäger ist nicht bean definiert und der falsche Discovery-Modus wird in der App verwendet wird. Wenn Sie Ihre beans.xml Datei

<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> 
zu verwenden, ändern

Dies wird es zwingen, alle Bohnen zu finden. Das Problem besteht darin, dass Ihre Basisklasse nicht erkannt wird und daher der Transaktionszustand nicht auf die Methode angewendet wird.

+0

Ich habe 'beans.xml' geändert, aber es ändert nichts, ich bekomme immer noch' javax.persistence.TransactionRequiredException'. Ich könnte zu EJB wechseln, aber ich wollte CDI ausprobieren. – Crozin

Verwandte Themen