简体   繁体   中英

@transactional annotation of Spring JPA don't work

I've a problem with transactions. I use Spring Jpa(1.8.2), Hibernate(4.3), MariaDB, Jboss EAP 6.2. I expect that the method saveExample() in the service class dosen't write nothing on the table because the method thows an exception and does rollback.

This is my code: 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");

    }


}

root-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";
    }
    .......
}   

EDIT 1 this is my 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>

What is wrong?

Both your configuration files contain the following line:

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

This line does exactly what it is told to do it will scan for components and it will do it for both the ContextLoaderListener and the DispatcherServlet . This results in 2 bean instances. The one picked up by the ContextLoaderListener will have proper transactions applied because in that same context is a <tx:annotation-driven /> however that isn't the case for the instance in the `DispatcherServlet.

Your controllers will use the bean instance registered in the closest application context, the one loaded by the DispatcherServlet so without transactions.

In short when doing component scanning you should take care of not scanning the same components twice. If you have a very broad base-package to scan you can use include and exclude filters.

In your root-context.xml use the following

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

In your servlet-context.xml use the following.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM