Spring @Transactional аннотации игнорируются

Мои аннотации @Transactionnal, похоже, игнорируются. У меня нет ошибок при инициализации контейнера Spring. Похоже, мой метод не был проксирован фреймворком Spring TX. Во время выполнения метода моей службы JDBCTemplate генерирует ожидаемое исключение RuntimeException. Проблема в том, что соединение JDBC не откатывается и изменения сохраняются. Трассировка стека не показывает никаких признаков прокси-сервера, который должен обернуть метод моей службы.

Изменить: добавлен код контроллера

Изменить 2: добавлен интерфейс службы

Вот мой сервисный интерфейс.

<code>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);
}
</code>

Вот мой сервис.

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

Spring Controller отвечает за вызов метода.

<code>@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";
    }
    ...
}
</code>

Вот моя весенняя конфигурация транзакции

<code><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>
</code>

Во время выполнения createApplication JDBCTemplate запускает RuntimeException, и транзакция не откатывается.

<code>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)
</code>
 Daniel Lavoie11 мая 2012 г., 11:41
Похоже, что у кого-то есть такая же проблема:stackoverflow.com/q/9930965/1387094
 Kevin Welker10 мая 2012 г., 19:01
Неважно, я только что видел в вашей трассировке стека, что он вызывается извне.
 Kevin Welker10 мая 2012 г., 18:58
ВашcreateApplication() метод вызывается другим методом в том же классе? Или это вызывается извне другим классом, который получаетApplicationService впрыскивается?
 Daniel Lavoie10 мая 2012 г., 23:08
Я хорошо знаю причину, почему у меня есть SQLException. Фактически я ожидаю, что это исключение вызовет откат моей транзакции, который не происходит.
 raddykrish10 мая 2012 г., 20:54
Ваш объект является объектомApplication имеет поле под названиемstatus, Пожалуйста, проверьте ограничения для этого на уровне базы данных. Требуются ли какие-либо значения по умолчанию? Какую базу данных вы используете? Было бы хорошо, если бы вы также опубликовали свой код сущности.

Ответы на вопрос(3)

Вам нужно определить интерфейс для@Transactional аннотации к работе:

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

И конкретный класс:

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

В качестве альтернативы, согласно комментарию Кевина Велкера, если вам не нужен интерфейс (хотя вам, вероятно, следует написать интерфейс), вы можете настроить использованиеproxy-target-class:

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

edit

Сообщение от вашегоSQLException является:

Field 'status' doesn't have a default value

Так что, возможно, вы переходите вnull где вы должны предоставить значение? В качестве альтернативы, проверьтеэта почта за некоторые странности, связанные с этой ошибкой.

 Daniel Lavoie10 мая 2012 г., 19:45
Я попытался использовать интерфейс и все еще иметь те же результаты. Я буду обновлять сделанные мной изменения.
 10 мая 2012 г., 19:19
Это не обязательно должен быть интерфейс, если вы настроили прокси класса. Увидетьthis answer for more
 05 июн. 2015 г., 16:42
Если вместе с рестлетами вам не нужен интерфейс и не нужно ничего настраивать, а только методы, которые вызываются из транзакций усиления рестлета.
Решение Вопроса

Я полагаю, что вы поместили свои сервисные компоненты в контекст, принадлежащий сервлету диспетчера, где должны жить только контроллеры, а затем вы объявили свои компоненты транзакции в корневом контексте. Автоматическое проксирование транзакций на основе аннотаций применяется только в пределах одного контекста, поэтому ваши сервисные компоненты в другом (неправильном) контексте не будут затронуты. Увидетьмой ответ на вопрос "Почему DispatcherServlet создает другой контекст приложения?" для более полного описания проблемы. Основная проблема заключается в том, что вы не понимаете, как организованы контексты в приложении Spring MVC.

 26 авг. 2014 г., 11:50
Может быть, вы можете указать на хороший текст, который объясняет, как организованы контексты? Спасибо :)

После трех дней отладки я наконец нашел причину, по которой мои аннотации были проигнорированы.

<tx:annotation-driven/> Инструкция, расположенная в дочернем файле контекста, не имеет доступа к bean-компонентам, созданным его родительским контекстом Spring.

Я должен был переместить его вmyapp-servlet.xml Используется моим диспетчером запросов.

Теперь он работает правильно.

 12 мая 2012 г., 17:16
Это не правильное решение. В лучшем случае это обходной путь, и вы неправильно поняли, какой контекст является родительским, а какой - дочерним. Увидетьmy answer для правильного объяснения / решения.

Ваш ответ на вопрос