Вопрос по java, transactions, spring – Spring @Transactional аннотации игнорируются

6

Мои аннотации @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>
Неважно, я только что видел в вашей трассировке стека, что он вызывается извне. Kevin Welker
Похоже, что у кого-то есть такая же проблема:stackoverflow.com/q/9930965/1387094 Daniel Lavoie
ВашcreateApplication() метод вызывается другим методом в том же классе? Или это вызывается извне другим классом, который получаетApplicationService впрыскивается? Kevin Welker
Я хорошо знаю причину, почему у меня есть SQLException. Фактически я ожидаю, что это исключение вызовет откат моей транзакции, который не происходит. Daniel Lavoie
Ваш объект является объектомApplication имеет поле под названиемstatus, Пожалуйста, проверьте ограничения для этого на уровне базы данных. Требуются ли какие-либо значения по умолчанию? Какую базу данных вы используете? Было бы хорошо, если бы вы также опубликовали свой код сущности. raddykrish

Ваш Ответ

3   ответа
10

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

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

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

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

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

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

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

Вам нужно определить интерфейс для@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 Lavoie
Это не обязательно должен быть интерфейс, если вы настроили прокси класса. Увидетьthis answer for more

Похожие вопросы