2012-05-10 2 views
6

Meine @ Transactionnal Annotationen scheint ignoriert zu werden. Ich habe keine Fehler bei der Initialisierung des Spring-Containers. Es sieht so aus, als ob meine Methode nicht von Spring TX Framework weitergegeben wurde. Während der Ausführung meiner Service-Methode wird eine erwartete RuntimeException von der JDBCTemplate ausgelöst. Das Problem besteht darin, dass die JDBC-Verbindung nicht zurückgesetzt wird und Änderungen beibehalten werden. Das Stacktrace zeigt kein Anzeichen des Proxys, der die Methode meines Diensts umbrechen soll.Spring @Transactional Annotations ignoriert

Edit: Hinzugefügt der Controller-Code

Edit 2: Added den Dienst des Interface-

Hier ist mein Service Interface.

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

Hier ist mein Service.

@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 verantwortlich für den Methodenaufruf.

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

Hier ist mein Frühling Transaktion Konfiguration

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

Während der Ausführung des Create, ein Runtime wird durch die JDBCTemplate ins Leben gerufen und die Transaktion nicht rollbacked.

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) 
+1

Wird Ihre 'createApplication()' - Methode von einer anderen Methode in derselben Klasse aufgerufen? Oder wird es von außen von einer anderen Klasse aufgerufen, die 'ApplicationService' injiziert bekommt? –

+0

Es macht nichts, ich sah gerade in Ihrem Stack-Trace, dass es extern aufgerufen wird. –

+0

Hat Ihr Entitätsobjekt 'Application' ein Feld namens' status'. Bitte überprüfen Sie die Einschränkungen in der Datenbankebene. Benötigt es irgendwelche Standardwerte? Welche Datenbank verwendest du? Es wäre gut, wenn Sie Ihren Entitätscode ebenfalls posten würden. – raddykrish

Antwort

9

Meine Vermutung ist, dass Sie Ihre Service-Beans in den Kontext des Dispatcher-Servlets gesetzt haben, in dem nur Controller-Beans leben sollen, und dann haben Sie Ihre Transaktionsbeans im Stammkontext deklariert. Die automatische Auto-Proxy-Funktion für Annotation-basierte Transaktionen wird nur in einem einzigen Kontext angewendet, sodass Ihre Service-Beans im anderen (falschen) Kontext nicht betroffen sind. Eine ausführlichere Beschreibung des Problems finden Sie unter my answer to "Why DispatcherServlet creates another application context?". Das Grundproblem besteht darin, dass Sie nicht verstehen, wie Kontexte in einer Spring MVC-Anwendung organisiert sind.

+4

Vielleicht können Sie auf guten Text zeigen, der erklärt, wie Kontexte organisiert sind? Vielen Dank :) –

5

Sie benötigen eine Schnittstelle definieren für die @Transactional Anmerkungen zu arbeiten:

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

Und die konkrete Klasse:

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

Alternativ pro Kevin Welker Kommentar, wenn nicht tun möchte eine Schnittstelle (obwohl Sie wahrscheinlich eine Schnittstelle schreiben sollten), können Sie die Verwendung proxy-target-class:

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

bearbeiten

Die Nachricht von Ihrem SQLException ist:

Field 'status' doesn't have a default value 

Also vielleicht in null vorbei Sie, wo Sie einen Wert bereitstellt werden sollte? Alternativ überprüfen Sie this post für einige Seltsamkeit im Zusammenhang mit diesem Fehler.

+4

Es muss nicht eine Schnittstelle sein, wenn Sie für die Klasse Proxy konfigurieren. Siehe [diese Antwort für mehr] (http://stackoverflow.com/a/4143586/433348) –

+0

Ich habe versucht, eine Schnittstelle zu verwenden und immer noch die gleichen Ergebnisse. Ich werde die Änderungen, die ich vorgenommen habe, aktualisieren. –

+0

Wenn Sie zusammen mit Restlets die Schnittstelle nicht benötigen, müssen Sie nur Methoden konfigurieren, die aus den Restlet-Gewinntransaktionen aufgerufen werden. – h22

2

Nach drei Tagen Debugging fand ich endlich den Grund, warum meine Anmerkungen ignoriert wurden.

Die in einer untergeordneten Kontextdatei enthaltene Anweisung <tx:annotation-driven/> hat keinen Zugriff auf die Beans, die vom übergeordneten Spring-Kontext erstellt wurden.

Ich musste es auf die myapp-servlet.xml von meinem Anfrage Dispatcher verwendet verschieben.

Jetzt funktioniert es richtig.

+0

Dies ist nicht die richtige Lösung. Es ist bestenfalls ein Workaround, und Sie haben falsch verstanden, welcher Kontext der übergeordnete und welcher untergeordnete ist. Siehe [meine Antwort] (http://stackoverflow.com/a/10564908/839646) für die korrekte Erklärung/Lösung. –