【问题标题】:@transactional annotation of Spring JPA don't workSpring JPA 的 @transactional 注释不起作用
【发布时间】:2015-09-07 18:02:53
【问题描述】:

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

这是我的代码: 持久性.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");

    }


}

根上下文.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>

怎么了?

【问题讨论】:

  • 如何调用 saveExample()?
  • 来自 Spring 控制器 (@Controller)
  • 如果你在 JBoss 中运行,使用 JBoss 中定义的 DataSource,你应该使用 JtaTransactionManager。
  • 我猜你也有一个*-servlet.xml,它也有一个&lt;context:component-scan /&gt;...
  • 问题是您要扫描所有内容两次。导致根上下文中的一个实例(使用 AOP 应用了适当的事务)和 servlet 上下文中的一个实例(没有应用事务),最后一个被使用。您的根上下文应该扫描除@Controllers 之外的所有内容,而servlet 上下文应该只扫描@Controllers。

标签: java spring hibernate jpa annotations


【解决方案1】:

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

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

这一行完全按照它的指示进行操作,它将扫描组件,并为ContextLoaderListenerDispatcherServlet 执行此操作。这会产生 2 个 bean 实例。 ContextLoaderListener 拾取的那个将应用适当的事务,因为在相同的上下文中是 &lt;tx:annotation-driven /&gt;,但是对于 `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>

【讨论】:

    猜你喜欢
    • 2019-06-15
    • 1970-01-01
    • 2019-12-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多