2016-11-29 22 views
0

我们有一个Spring应用程序版本4.3.3.RELEASE使用Hibernate 5.1.0.Final和Java 8 该应用程序有一个rmi接口,它向Java胖客户端应用程序公开服务。该应用程序还公开了Spring Restful Webservices。 导致我们遇到问题的服务作为rmi服务暴露在外。以RMI方式调用服务工作得很好,但调用restful ws会产生javax.persistence.TransactionRequiredException,如下所示。Spring rmi服务的工作原理并不像平静ws

我以为我找到了答案,这件事情在这里,但我似乎无法得到正确的事情: Spring @Transactional not working

We'we尝试过许多不同的东西,比如:

@Transactional(propagation = Propagation.REQUIRES_NEW) 

但我猜测这里的问题是我有两个spring-config文件,但没有在我的springrest-config.xml中定义正确的东西。

有谁知道如何解决这个问题?

这里是DAO方法:

@Override 
    public void deleteFormReplyForInvitation(Long id) { 

     try { 
      entityManager.createQuery("DELETE FROM FormReply where id = " + id).executeUpdate(); 


     } catch (Exception e) { 
      throw new RuntimeException(e); 
     } 
    } 

这里是服务代码:

@Override 
@Transactional 
public String withdrawConsent(final String username, final Patient patient, final String method) { 

try { 
    List<Invitation> invitations = invitationService.getInvitationsForPatient(patient); 
    for (Invitation invitation : invitations) { 
     if (invitation.getFormReply() != null) { 
      formReplyService.deleteFormReply(invitation.getFormReply().getId()); 
     } 
     if (!invitation.getReminders().isEmpty()) { 
      reminderService.deleteReminders(invitation); 
     } 
     invitationService.deleteInvitation(invitation.getId()); 
    } 

    eventlogService.deleteEventlog(patient); 


    EntryKind entryKind = entryKindService.getEntryKind(1); 
    eventlogService.addEventlog(patient, eventlogDataDeleted, entryKind, true, username); 

    addConsent(patient, method, username); 

    patient.setActive(false); 
    patientService.saveOrUpdatePatient(patient); 

    return "OK"; 


} catch (Exception e) { 
    logger.error("Feil oppstått i withdrawConsent.", e); 
    return "ERROR"; 

} 
} 

@Override 
    @Transactional 
    public String withdrawConsent(final String birthNumber, final String programCode) { 
     String encryptedPNR = encryptionService.encryptDBQ(birthNumber); 
     Cancertype cancertype = cancertypeService.getCancertypeByCode(programCode); 
     Patient aPatient = patientService.getPatient(encryptedPNR, cancertype); 

     return withdrawConsent("WebService", aPatient, "Webservice"); 
    } 

这里是其余控制器:

@RequestMapping(value = "/withdrawconsent", method = RequestMethod.GET) 
    @Transactional 
    String withdrawConsent(@RequestParam("programcode") final String programcode, @RequestParam("birthnumber") final String birthnumber) throws CancertypeException { 
     return consentService.withdrawConsent(birthnumber, programcode); 
    } 

这里是弹簧配置。 xml:

<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans" 
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xmlns:context="http://www.springframework.org/schema/context" 
     xmlns:tx="http://www.springframework.org/schema/tx" xmlns:util="http://camel.apache.org/schema/spring/v2.15" 
     xmlns:mvc="http://www.springframework.org/schema/mvc" 

     xsi:schemaLocation="http://www.springframework.org/schema/beans 
          http://www.springframework.org/schema/beans/spring-beans-4.1.xsd 
          http://www.springframework.org/schema/context 
          http://www.springframework.org/schema/context/spring-context-4.1.xsd 
          http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd" 
     default-autowire="byName"> 


    <!-- Root Context: defines shared resources visible to all other web components --> 
    <context:component-scan base-package="net.krg.proms" /> 
    <context:annotation-config /> 



    <!-- Define all property files here. Values will be available in all other spring config files. --> 
    <bean id="placeholderConfig" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> 
     <property name="locations"> 
      <list> 
       <value>classpath:db.properties</value> 
       <value>classpath:system.properties</value> 
      </list> 
     </property> 
    </bean> 

    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" name="EntityManagerFactory"> 
     <property name="packagesToScan" value="net.krg.proms" /> 
     <property name="persistenceUnitName" value="proms"></property> 
     <property name="dataSource" ref="dataSource"></property> 
     <property name="jpaVendorAdapter"> 
      <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> 
       <property name="showSql" value="${db.show_sql}" /> 
       <property name="generateDdl" value="false" /> 
       <property name="databasePlatform" value="${db.dialect}" /> 
      </bean> 
     </property> 

    </bean> 

<!-- Values are defined in db.properties --> 
<!--<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">--> 
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> 
    <property name="driverClassName" value="${db.driver}" /> 
    <property name="url" value="${db.url}" /> 
    <property name="username" value="${db.username}" /> 
    <property name="password" value="${db.password}" /> 
</bean> 

<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
    <property name="dataSource" ref="dataSource"/> 
</bean> 

<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean"> 
    <property name="dataSource" ref="dataSource"/> 
    <property name="packagesToScan"> 
     <list> 
      <value>net.krg.proms.domain.proms</value> 
     </list> 
    </property> 
    <property name="hibernateProperties"> 
     <props> 
      <prop key="hibernate.dialect">${db.dialect}</prop> 
      <prop key="hibernate.show_sql">${db.show_sql}</prop> 
      <prop key="hibernate.hbm2ddl.auto">${db.hbm2ddl.auto}</prop> 
     </props> 

    </property> 
</bean> 

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager" name="TransactionManager"> 
    <property name="entityManagerFactory" ref="entityManagerFactory"></property> 
    <property name="persistenceUnitName" value="proms"/> 
</bean> 


<bean id="entityManagerFactory2" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" name="EntityManagerFactory2"> 
    <property name="packagesToScan" value="net.krg.proms.domain.hdb" /> 
    <property name="persistenceUnitName" value="hdb"></property> 
    <property name="dataSource" ref="dataSource2"></property> 
    <property name="jpaVendorAdapter"> 
     <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> 
      <property name="showSql" value="${db.hdb.show_sql}" /> 
      <property name="generateDdl" value="false" /> 
      <property name="databasePlatform" value="${db.hdb.dialect}" /> 
     </bean> 
    </property> 

</bean> 
<!-- Values are defined in db.properties --> 
<!--<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">--> 
<bean id="dataSource2" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> 
    <property name="driverClassName" value="${db.hdb.driver}" /> 
    <property name="url" value="${db.hdb.url}" /> 
    <property name="username" value="${db.hdb.username}" /> 
    <property name="password" value="${db.hdb.password}" /> 
</bean> 

<bean id="txManager2" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
    <property name="dataSource" ref="dataSource2"/> 
</bean> 

<bean id="sessionFactory2" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean"> 
    <property name="dataSource" ref="dataSource2"/> 
    <property name="packagesToScan"> 
     <list> 
      <value>net.krg.proms.domain.hdb</value> 
     </list> 
    </property> 
    <property name="hibernateProperties"> 
     <props> 
      <prop key="hibernate.dialect">${db.hdb.dialect}</prop> 
      <prop key="hibernate.show_sql">${db.hdb.show_sql}</prop> 
      <!-- <prop key="hibernate.hbm2ddl.auto">${db.hdb.hbm2ddl.auto}</prop>--> 
     </props> 

    </property> 
</bean> 

<bean id="transactionManager2" class="org.springframework.orm.jpa.JpaTransactionManager" name="TransactionManager2"> 
    <property name="entityManagerFactory" ref="entityManagerFactory2"></property> 
    <property name="persistenceUnitName" value="hdb"/> 
</bean> 

<tx:annotation-driven /> 
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" /> 


<!--RMI starts Here--> 
<!--this is the only bean that should be use expose all the needed RMI sercices which GUI will consume--> 
<bean id="rmiServices" class="net.krg.proms.services.RMIServiceImpl"/> 
<bean id="remoting_MessageSpecificationService_Proxy_Exporter" class="org.springframework.remoting.rmi.RmiServiceExporter"> 
    <property name="service" ref="rmiServices"/> 
    <property name="serviceInterface" value="net.krg.proms.rmiInterfaces.RMIServices"/> 
    <property name="serviceName" value="rmiServices"/> 
    <property name="registryPort" value="10998"/> 
    <property name="alwaysCreateRegistry" value="false"/> 
</bean> 
<!--RMI end here--> 


<!--include camel configuraiton here --> 
<!--load camel context--> 
<import resource="camel-config.xml"/> 

这里是弹簧REST风格的WS的配置:

<beans xmlns="http://www.springframework.org/schema/beans" 
     xmlns:context="http://www.springframework.org/schema/context" 
     xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xsi:schemaLocation=" http://www.springframework.org/schema/beans 
     http://www.springframework.org/schema/beans/spring-beans-4.1.xsd 
     http://www.springframework.org/schema/context 
     http://www.springframework.org/schema/context/spring-context-4.1.xsd 
     http://www.springframework.org/schema/mvc 
     http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd"> 

    <mvc:annotation-driven/> 
    <context:component-scan base-package="net.krg.proms.services" /> 
    <context:annotation-config></context:annotation-config> 
</beans> 

下面是web.xml中:

<?xml version="1.0" encoding="UTF-8"?> 
<web-app metadata-complete="true" 
     xmlns="http://java.sun.com/xml/ns/javaee" 
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
      http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 
      version="2.5"> 


    <listener> 
     <listener-class> 
      org.springframework.web.context.ContextLoaderListener 
     </listener-class> 
    </listener> 

    <context-param> 
     <param-name>contextConfigLocation</param-name> 
     <param-value>classpath*:/spring-config.xml</param-value> 
    </context-param> 
<!----> 

    <!--ws--> 
    <servlet> 
     <servlet-name>springrest</servlet-name> 
     <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 
     <init-param> 
      <param-name>contextConfigLocation</param-name> 
      <param-value>/WEB-INF/springrest-servlet.xml</param-value> 
     </init-param> 
     <load-on-startup>1</load-on-startup> 
    </servlet> 

    <servlet-mapping> 
     <servlet-name>springrest</servlet-name> 
     <url-pattern>/</url-pattern> 
    </servlet-mapping> 


</web-app> 

我们用来调用WebService的我的URL:■ http://xxx-xxx-xxx:8080/proms-services-1.0-SNAPSHOT/withdrawconsent?birthnumber=01016512345&programcode=PR

调用rest-ws时的stacktrace:

*36860 [http-nio-8080-exec-34] ERROR net.krg.proms.services.impl.ConsentServiceImpl - Feil oppst?tt i withdrawConsent. 
java.lang.RuntimeException: javax.persistence.TransactionRequiredException: Executing an update/delete query 
     at net.krg.proms.dao.impl.FormReplyDaoImpl.deleteFormReplyForInvitation(FormReplyDaoImpl.java:61) 
     at net.krg.proms.services.impl.FormReplyServiceImpl.deleteFormReply(FormReplyServiceImpl.java:36) 
     at net.krg.proms.services.impl.ConsentServiceImpl.withdrawConsent(ConsentServiceImpl.java:72) 
     at net.krg.proms.services.impl.ConsentServiceImpl.withdrawConsent(ConsentServiceImpl.java:110) 
     at net.krg.proms.services.ws.krgcommon.PatientInfoController.withdrawConsent(PatientInfoController.java:77) 
     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:498) 
     at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:221) 
     at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136) 
     at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:114) 
     at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827) 
     at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738) 
     at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) 
     at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963) 
     at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897) 
     at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) 
     at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861) 
     at javax.servlet.http.HttpServlet.service(HttpServlet.java:622)* 
+0

由于组件扫描,您正在创建2个服务实例。您的servlet应该只扫描与Web相关的bean('@ Controller')并忽略其余部分,因为它将由'ContextLoaderListener'加载。您现在有两个服务实例,正确的是来自父上下文(“ContextLoaderListener”)的实例,错误的是“DispatcherServlet”中的on。 –

+0

谢谢@M-Denium,解决了这个问题。它导致我们遇到了一个新问题(将一个集合与两个开放会话相关联的非法尝试)我们通过使用合并而不是更新实体来解决这个问题。 –

回答

0

我不知道它会帮助,但据我所知,事务管理只属于业务层,也没有持久性也不控制器。也许它会在通过rmi公开@Transaction方法的同时引发某种冲突。