Spring事物失效、無效問題
專案原來的ORM是Hibernate,要改造成MyBatis,相關配置後啟動成功,沒有報錯。
資料庫是MySql5.7 表的儲存引擎是:InnoDB
但是在測試事物有效性的時候,發現不回滾!!
@Override
public void saveUser(UserVO vo) {
UserDao.saveUser(vo);
throw new RuntimeException("事物異常,請回滾吧。");
}
配置如下:
<!-- 啟用註解支援 --> <context:annotation-config/> <!-- 開啟aop註解方式 --> <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
<!-- ========== Mybatis 配置以及事物的配置 Start ==========--> <!-- 不需要mybatis的配置對映檔案 --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 注入資料來源--> <property name="dataSource" ref="dataSource" /> <!-- 自動掃描對映的xml檔案 --> <property name="mapperLocations" value="classpath:com/xx/*/dao/mapper/*.xml"></property> </bean> <!-- 自動掃描對映介面類 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property> <property name="basePackage" value="com/xx/*/dao"></property> </bean> <!-- 配置Spring的事務管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!-- 攔截器方式配置事物 --> <tx:advice id="transactionAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="save*" propagation="REQUIRED" read-only="false" /> <tx:method name="add*" propagation="REQUIRED" read-only="false" /> <tx:method name="delete*" propagation="REQUIRED" read-only="false" /> <tx:method name="update*" propagation="REQUIRED" read-only="false" /> <tx:method name="edit*" propagation="REQUIRED" read-only="false" /> <tx:method name="get*" propagation="SUPPORTS" read-only="true" /> <tx:method name="find*" propagation="SUPPORTS" read-only="true" /> <tx:method name="export*" propagation="SUPPORTS" read-only="true" /> <tx:method name="*" propagation="SUPPORTS" read-only="true" /> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="transactionPointcut" expression="execution(* com.xx.xx.service.impl.*.*(..))" /> <aop:advisor advice-ref="transactionAdvice" pointcut-ref="transactionPointcut" /> </aop:config> <!--支援註解驅動的事務管理,指定預設事務管理器 --> <tx:annotation-driven order="0" transaction-manager="transactionManager"/> <!-- ========== Mybatis 配置以及事物的配置 End ==========-->
最終的問題出現在component-scan的順序上:
大致是這樣的場景:
專案中原來使用Spring MVC頁面採用JSP。想要優化,但並不打算用其他的模板語言:比如Freemarker、Thymeleaf等。
直接使用HTML檔案,多好啊~,後來我想不需要Controller層了!!模板+資料的形式太死板了。HTML+Restful 靈活多了。
直接就是dao、dao.mapper 、service、service.impl、vo
@RequestMapping 就寫在了Service實現層
在applicationContext.xml 中配置自動掃描:
<context:component-scan base-package="com.xx">
啟動正常,但是url訪問卻是404。(;′⌒`)
於是將這段掃描配置移至spring-mvc.xml,能夠正常訪問了。 但是Spring的事物失效了!!
web.xml中的配置:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<servlet>
<servlet-name>dispatcherServlet</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>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
spring-mvc.xml後面載入的,所以事物的特性被覆蓋了。
最終還是使用了,dao、dao.mapper、service、service.impl、controller、vo的結構。
service在applicationContext.xml中掃描,事物也是配置在這個xml檔案裡面的。
<!-- 配置自動掃描的包 -->
<context:component-scan base-package="com">
<!-- 掃描時跳過 @Controller 註解的JAVA類(控制器) -->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
controller在spring-mvc.xml中掃描
<!-- controller類支援事物@Transactional -->
<tx:annotation-driven/>
<!--而在springMVC配置檔案中將Service註解給去掉-->
<context:component-scan base-package="com">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service" />
</context:component-scan>
問題解決,測試事物成功。
controller層,也支援事物,使用註解形式配合,很棒。示例如下:
@Transactional
@ResponseBody
@RequestMapping(value = "/saveUser", method = RequestMethod.GET)
public void saveUser() throws IOException {
Uservo = new Uservo();
vo.setUserName("很好");
vo.setUserBirthday(new Date());
vo.setUserId("6");
vo.setUserSalary(500d);
userService.saveUser(vo);
vo.setUserId("7");
userService.saveUser(vo);
throw new RuntimeException("事物異常,請回滾吧。");
}
後來仔細想了想,程式碼的分層。還是有意義的。controller可以處理頁面邏輯,比如引數接收、處理、url對映這些偏頁面的邏輯。service層則更加註重業務邏輯處理。這樣分開也是很好的。