Spring rmi服务有效,但不是一个宁静的ws

时间:2016-11-29 14:53:30

标签: java spring rest transactional

我们有一个使用Hibernate 5.1.0.Final和Java 8的Spring应用程序版本4.3.3.RELEASE 该应用程序有一个rmi接口,它将服务公开给Java胖客户端应用程序。该应用程序还公开了Spring Restful Webservices。 导致我们出现问题的服务被曝光为rmi服务一个宁静的ws。将服务称为RMI非常有用,但调用restful ws会产生一个javax.persistence.TransactionRequiredException,如下所示。

我以为我在这里找到了这个问题的答案,但我似乎无法把事情弄清楚: Spring @Transactional not working

我们尝试过很多不同的事情,比如:

@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);
    }

这是spring-config.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"/>

以下是spring-restful 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>

我们用来调用网络服务i的网址 http://xxx-xxx-xxx:8080/proms-services-1.0-SNAPSHOT/withdrawconsent?birthnumber=01016512345&programcode=PR

调用rest-ws时的堆栈跟踪:

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

1 个答案:

答案 0 :(得分:0)

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