我有一个Spring MVC Controller方法,它被标记为“Transactional”,它使得几个服务调用也被标记为“Transactional”,但是它们被视为独立事务,并且根据我的需要单独提交而不是全部在一个事务下。
根据调试输出,看起来Spring只是在到达服务调用时创建事务。我将只为他们看到输出“使用名称创建新事务...”而不是调用它们的控制器方法的输出。
我有什么明显的遗失吗?
下面的示例代码(略),控制器:
@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
}
服务代码:
public interface SomeService {
@Transactional
public void doInsert (SomeObject blah);
}
我尝试从服务调用中删除Transactional标记,可能是我搞砸了,我告诉它为每个调用创建一个,但是在调试输出中没有创建事务。
结果是,一旦我得到强制异常并检查表,插件已经提交而不是像我想要的那样回滚。
那我做错了什么?
为什么Spring会忽略控制器上的Transactional标记?
根据评论者的要求发布我的上下文的相关部分:
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>
消费满-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"/>
嗯,那是感兴趣和无意的 - 我有重复的组件扫描。这可能导致问题吗?
答案 0 :(得分:2)
我认为Spring在这里忽略了@Transactional注释,因为它为事务创建了一个代理,但是调度程序没有通过代理调用控制器。
在the Spring MVC documentation,17.3.2中有一个关于注释控制器的有趣注释,它没有描述你的确切问题,但表明这种方法存在问题:
使用带注释的控制器类时常见的陷阱 在应用需要创建代理的功能时发生 控制器对象(例如@Transactional方法)。通常你会 为控制器引入一个接口,以便使用JDK动态 代理。要使其工作,您必须移动@RequestMapping 注释,以及任何其他类型和方法级注释 (例如@ModelAttribute,@ InitBinder)到接口以及 映射机制只能“看到”代理公开的接口。 或者,您可以在中激活proxy-target-class =“true” 应用于控制器的功能的配置(在我们的 交易场景)。这样做表明 应该使用基于CGLIB的子类代理而不是 基于接口的JDK代理。有关各种代理的更多信息 机制见第9.6节“代理机制”。
我认为你最好创建另一个服务来包装现有服务并注释它。