@transactional注释Spring JPA不起作用

时间:2015-09-07 18:02:54

标签: java spring hibernate jpa annotations

我遇到了交易问题。我使用Spring Jpa(1.8.2),Hibernate(4.3),MariaDB,Jboss EAP 6.2。 我希望服务类中的saveExample()方法不会在表上写任何内容,因为该方法会抛出异常并进行回滚。

这是我的代码: 的persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
    <persistence-unit name="fooPU">
    .........
        <class>ExampleCass</class>
        <properties>
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
            <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
            <property name="hibernate.enable_lazy_load_no_trans" value="true"/>
        </properties>
    </persistence-unit>
</persistence>

Service.java

import org.hibernate.Hibernate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;


@Service
@Transactional
public class ExampleService {
    private TableExampleRepository exampleRepo;
    private JpaTransactionManager transactionManager;

    @Autowired
    public ExampleService(TableExampleRepository exampleRepo) {
        this.exampleRepo = exampleRepo;
    }



    @Transactional(rollbackFor={Exception.class})
    public void saveExample(ExampleClass example ){

        exampleRepo.save(example);
        throw new RuntimeException("foo");

    }


}

根context.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:ldap="http://www.springframework.org/schema/ldap"
    xmlns:jpa="http://www.springframework.org/schema/data/jpa"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:jee="http://www.springframework.org/schema/jee"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.1.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
        http://www.springframework.org/schema/data/repository http://www.springframework.org/schema/data/repository/spring-repository-1.7.xsd
        http://www.springframework.org/schema/ldap http://www.springframework.org/schema/ldap/spring-ldap-2.0.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.1.xsd
        http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">


    <context:property-placeholder
        location="file:///${jboss.modules.dir}/system/layers/base/...../foo.properties" />

     <jpa:repositories base-package="it.foo.repository"
        entity-manager-factory-ref="entityManagerFactory"
        transaction-manager-ref="transactionManager"/> 

    <bean id="entityManagerFactory" name="foo"
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="datasource" /> 
        <property name="persistenceUnitName" value="fooPU" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" >             
                <property name="showSql" value="false" />
                <property name="generateDdl" value="false" />
                <property name="database" value="MYSQL"/>
                <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />
            </bean>
        </property>
             </bean>    

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>
    <tx:annotation-driven transaction-manager="transactionManager" />
    <!-- Data Source -->
    <jee:jndi-lookup jndi-name="java:jboss/datasources/fooDS"
        id="datasource" expected-type="javax.sql.DataSource" />




    <bean id="messageSource"
        class="org.springframework.context.support.ResourceBundleMessageSource">
        <property name="basename" value="message" />
    </bean>

    <bean id="multipartResolver"
        class="org.springframework.web.multipart.commons.CommonsMultipartResolver" />

    <context:component-scan base-package="foo" />

</beans>

Controller.java

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.core.env.Environment;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttributes;

import com.fasterxml.jackson.databind.ObjectMapper;

@Controller
@SessionAttributes("att1")
public class ExampleController extends ReportController {
    private ExampleService exampleService;

    @Autowired
    public ExampleController(ExampleService exampleService) {
        this.exampleService = exampleService;
    }
    .......
    @RequestMapping(value = "/salvaExample", method = RequestMethod.GET)
    public String visualizzaProspetto(@RequestParam ExampleClass example,Model model) {
        exampleService.saveExample( example); 
        return "page1";
    }
    .......
}   

编辑1 这是我的servlet-context.xml

    <?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:beans="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">

    <!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->

    <!-- Enables the Spring MVC @Controller programming model -->
    <annotation-driven />

    <!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
    <resources mapping="/resources/**" location="/resources/" />

    <!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
    <beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <beans:property name="prefix" value="/WEB-INF/views/" />
        <beans:property name="suffix" value=".jsp" />
    </beans:bean>

    <context:component-scan base-package="foo" />

    <mvc:resources mapping="/resources/**" location="/resources/" />
    <mvc:resources mapping="/webjars/**" location="classpath:/WEB-INF/resources/webjars/" />

</beans:beans>

有什么问题?

1 个答案:

答案 0 :(得分:3)

您的配置文件都包含以下行:

<context:component-scan base-package="foo" />

这一行完全按照它所要求的方式执行,它将扫描组件,它将为ContextLoaderListenerDispatcherServlet执行此操作。这导致2个bean实例。 ContextLoaderListener选择的那个将应用正确的事务,因为在同一个上下文中是<tx:annotation-driven />,但是`DispatcherServlet中的实例不是这种情况。

您的控制器将使用在最近的应用程序上下文中注册的bean实例,DispatcherServlet加载的那个实例,因此没有事务。

简而言之,在进行组件扫描时,您应该注意不要扫描相同的组件两次。如果您要进行非常广泛的base-package扫描,则可以使用包含和排除过滤器。

root-context.xml中使用以下

<context:component-scan base-package="foo">
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>

servlet-context.xml中使用以下内容。

<context:component-scan base-package="foo" use-default-filters="false">
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>