2015-06-23 34 views
9

具有以下,相當簡單的代碼並正確配置採用JTA的持久化上下文:@Transactional忽視的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> { 

} 

我遇到以下異常而調用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錯誤,其中攔截器不是bean定義,並且在您的應用程序中使用了錯誤的發現模式。如果你改變你的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