【问题标题】:Spring @Transactional annotations ignoredSpring @Transactional 注释被忽略
【发布时间】:2012-05-19 06:46:07
【问题描述】:

我的@Transactionnal 注释似乎被忽略了。我对 Spring 容器的初始化没有错误。看起来我的方法没有被 Spring TX 框架代理。在执行我的服务方法期间,JDBCTemplate 会抛出预期的 RuntimeException。问题是 JDBC 连接没有回滚并且更改被保留。堆栈跟踪没有显示应该包装我的服务方法的代理的任何迹象。

编辑:添加控制器的代码

编辑 2:添加服务接口

这是我的服务接口。

public interface ApplicationsService {
    public Application getApplicationById(int id);

    public void createApplication(Application application);

    public void createInstance(Application application);

    public Map<Integer, Application> getUserApplications(String username);

    public Application newApplication(String email);
}

这是我的服务。

@Service
public class ApplicationsServiceImpl implements ApplicationsService {
    ...
    @Transactional
    public void createApplication(Application application){
        // Persisting the application.
        applicationDAO.createApplication(application);
        application.setId(
            applicationDAO.findApplicationId(application.getName(), application.getAccount().getEmail())
        );

        // Creating the physical instance.
        createInstance(application);
    }
    ...
}

Spring Controller 负责方法调用。

@Controller
@RequestMapping("/applications")
public class ApplicationsController {
    ...
    @Autowired
    private ApplicationsService applicationsService;
    ...

    @RequestMapping(method=RequestMethod.POST)
    public String saveApplication(
        @Valid Application application, 
        BindingResult bindingResult, 
        Principal principal
    ){  
        application.setAccount(this.accountService.getAccount(principal.getName()));
        this.applicationsService.createApplication(application);

        return "application/creatingApplication";
    }
    ...
}

这是我的 Spring 事务配置

<beans
    xmlns="http://www.springframework.org/schema/beans"
    xmlns:tx="http://www.springframework.org/schema/tx"
    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-3.1.xsd

                            http://www.springframework.org/schema/tx
                            http://www.springframework.org/schema/tx/spring-tx-3.1.xsd"
>
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="DADataSource"/>
    </bean>

    <tx:annotation-driven />
</beans>

在 createApplication 执行过程中,JDBCTemplate 会引发 RuntimeException 并且事务不会回滚。

GRAVE: Servlet.service() for servlet [DACloudWeb] in context with path [/DACloudWeb] threw exception [Request processing failed; 
nested exception is org.springframework.jdbc.UncategorizedSQLException: PreparedStatementCallback; uncategorized SQLException for SQL [INSERT INTO instances (  serverId,   appId,  lastDeployment ) VALUES (   ?,?,? ) ]; SQL state [HY000]; error code [1364]; Field 'status' doesn't have a default value; nested exception is java.sql.SQLException: Field 'status' doesn't have a default value] with root cause
    java.sql.SQLException: Field 'status' doesn't have a default value
        at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1073)
        at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3609)
        at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3541)
        at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2002)
        at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2163)
        at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2624)
        at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2127)
        at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2427)
        at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2345)
        at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2330)
        at org.apache.tomcat.dbcp.dbcp.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:105)
        at org.apache.tomcat.dbcp.dbcp.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:105)
        at org.springframework.jdbc.core.JdbcTemplate$2.doInPreparedStatement(JdbcTemplate.java:818)
        at org.springframework.jdbc.core.JdbcTemplate$2.doInPreparedStatement(JdbcTemplate.java:1)
        at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:587)
        at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:812)
        at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:868)
        at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:876)
        at com.cspinformatique.dacloudWeb.applications.dao.InstanceJDBCDAO.createInstance(InstanceJDBCDAO.java:50)
        at com.cspinformatique.dacloudWeb.applications.service.InstanceService.createInstance(InstanceService.java:42)
        at com.cspinformatique.dacloudWeb.applications.service.ApplicationsService.createInstance(ApplicationsService.java:63)
        at com.cspinformatique.dacloudWeb.applications.service.ApplicationsService.createApplication(ApplicationsService.java:52)
        at com.cspinformatique.dacloudWeb.applications.controller.ApplicationsController.saveApplication(ApplicationsController.java:64)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:213)
        at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:126)
        at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:96)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:617)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:578)
        at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:923)
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:852)
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)
        at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:789)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:641)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:311)
        at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:116)
        at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:83)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:323)
        at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:323)
        at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:101)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:323)
        at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:113)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:323)
        at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:54)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:323)
        at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:323)
        at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilter(BasicAuthenticationFilter.java:150)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:323)
        at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:182)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:323)
        at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:105)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:323)
        at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:323)
        at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:173)
        at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
        at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:259)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:224)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:169)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:168)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98)
        at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:927)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
        at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:987)
        at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:579)
        at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:309)
        at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
        at java.lang.Thread.run(Thread.java:680)

【问题讨论】:

  • 您的createApplication() 方法是否被同一类中的不同方法调用?还是从外部调用它,由另一个注入ApplicationService 的类?
  • 没关系,我刚刚在您的堆栈跟踪中看到它正在被外部调用。
  • 您的实体对象Application 是否有一个名为status 的字段。请检查数据库级别的约束。它是否需要任何默认值?您使用的是哪个数据库?如果您也发布您的实体代码会很好。
  • 我很清楚我有 SQLException 的原因。事实上,我希望这个异常会触发我的事务回滚,这不会发生。
  • 看起来其他人也有同样的问题:stackoverflow.com/q/9930965/1387094

标签: java spring jdbc transactions annotations


【解决方案1】:

我的猜测是您已经将服务 bean 放在属于调度程序 servlet 的上下文中,其中应该只有控制器 bean,然后您已经在根上下文中声明了事务 bean。基于注释的事务自动代理仅适用于单个上下文,因此您在另一个(错误)上下文中的服务 bean 不会受到影响。有关问题的更完整描述,请参阅 my answer to "Why DispatcherServlet creates another application context?"。根本问题是您不了解 Spring MVC 应用程序中的上下文是如何组织的。

【讨论】:

  • 也许你可以指出好的文本来解释上下文是如何组织的?谢谢你:)
【解决方案2】:

您需要为@Transactional 注解定义一个接口才能工作:

public interface ApplicationsService {
    public void createApplication(Application application);
}

具体类:

@Service
public class ApplicationsServiceImpl {
    @Transactional
    public void createApplication(Application application) {
        // ...
    }
}

或者,根据 Kevin Welker 的评论,如果不想要接口(尽管您可能应该编写接口),您可以配置使用 proxy-target-class

<tx:annotation-driven proxy-target-class="true" />

编辑

来自您SQLException 的消息是:

Field 'status' doesn't have a default value

所以也许你传入null 你应该在哪里提供一个值?或者,检查this post 以了解与此错误相关的一些奇怪之处。

【讨论】:

  • 它不必是一个接口,如果你配置类代理。见this answer for more
  • 我尝试使用界面,但仍然有相同的结果。我会更新我所做的更改。
  • 如果和restlet一起使用,你不需要接口,也不需要配置任何东西,只需要从restlet调用的方法获得事务。
【解决方案3】:

经过三天的调试,终于找到了我的注解被忽略的原因。

位于子上下文文件中的&lt;tx:annotation-driven/&gt; 指令无权访问由其父 Spring 上下文创建的 bean。

我必须将它移动到我的请求调度程序使用的myapp-servlet.xml

现在,它工作正常。

【讨论】:

  • 这不是正确的解决方案。这充其量只是一种解决方法,并且您误解了哪个上下文是父级,哪个是子级。有关正确的解释/解决方案,请参阅my answer
猜你喜欢
  • 1970-01-01
  • 2013-11-05
  • 1970-01-01
  • 2023-03-24
  • 2019-02-18
  • 2013-04-02
  • 2014-10-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多