I have a Spring MVC Controller method which is tagged as "Transactional" which makes several service calls which are also tagged "Transactional" but they are treated as independent transactions and are committed separately instead of all under one transaction as I desire.
Based on the debug output, it appears Spring is only creating transactions when it reaches the service calls. I will see the output "Creating new transaction with name..." only for them but not one for the controller method who calls them.
Is there something obvious I am missing?
Sample code (abbreviated) below, controller:
@Controller
public class BlahBlah etc...
@Autowired
SomeService someService;
@Transactional
@RequestMapping(value="windows/submit", method=RequestMethod.POST)
@ResponseBody
public String submit (@RequestBody SomeObject blah) throws Exception {
someService.doInsert(blah);
if (true) throw Exception("blah");
... other stuff
}
service code:
public interface SomeService {
@Transactional
public void doInsert (SomeObject blah);
}
I tried removing the Transactional tag from the service call thinking maybe I messed it up and I am telling it to create one for each call but then in the debug output no transaction is created.
So the result is once I get my forced exception and check the table, the insert has committed instead of rolled back like I want.
So what did I do wrong?
Why is Spring ignoring the Transactional tag on the controller?
Posting relevant part of my context as per request from commenter:
Web.xml:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:spring.xml
classpath:spring-security.xml
classpath:spring-datasource.xml
</param-value>
</context-param>
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
sping-mvc.xml:
<context:annotation-config/>
<context:component-scan base-package="com.blah"/>
<mvc:annotation-driven/>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver" p:order="1" />
... non-relevent stuff
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean id="webContentInterceptor" class="org.springframework.web.servlet.mvc.WebContentInterceptor">
<property name="cacheSeconds" value="0"/>
<property name="useExpiresHeader" value="true"/>
<property name="useCacheControlHeader" value="true"/>
<property name="useCacheControlNoStore" value="true"/>
</bean>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean id="httpInterceptor" class="com.blah.BlahInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
<mvc:view-controller path="/" view-name="blah"/>
<context:component-scan base-package="com.blah"/>
Hmm, thats interested and unintended - I have component scan duplicated. Could that be causing problems with this?
I think Spring ignores the @Transactional annotation here because it creates a proxy for the transaction, but the dispatcher isn't calling the controller through the proxy.
There's an interesting note in the Spring MVC documentation, 17.3.2, about annotating controllers, it doesn't describe your exact problem but shows that there are problems with this approach:
I think you'd be better off creating another service to wrap the existing ones and annotating that.