Mybatis + SpringMVC事务管理
2017-10-09 21:12
537 查看
版权声明:本文为博主原创文章,未经博主允许不得转载。
SpringMVC支持声明式和编程式事务管理,这里我讲的是声明式事务,也即通过注解@Transactional来使用事务。
这里是我在这个工程中所使用的jar包:http://download.csdn.net/detail/liujan511536/9050079
这里是我这个工程的源码:http://download.csdn.net/detail/liujan511536/9050093
这是一个dynamic web project。
首先看配置文件web.xml,这个文件在WebContent/WEB-INF/目录下:
[html]
view plain
copy
print?
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<display-name>SpringMVC_Mybatis</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<!-- 读取mybatis配置文件 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:mybatis-context.xml</param-value>
</context-param>
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<!-- 加载springmvc的xml到spring的上下文容器中 -->
<param-name>contextConfigLocation</param-name>
<param-value>
<!-- /WEB-INF/classes/application-context.xml -->
classpath:application-context.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>
</web-app>
mybatix-context.xml (/WebContent/WEB-INF/classes/目录下):
[html]
view plain
copy
print?
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="config" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:db-config.properties</value>
</list>
</property>
</bean>
<!-- 配置数据库dataSource -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driverClass}" />
<property name="username" value="${jdbc.username}" />
<property name="url" value="${jdbc.url}" />
</bean>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="typeAliasesPackage" value="com.liujan.entity" />
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.liujan.mapper" />
</bean>
</beans>
application-context.xml (/WebContent/WEB-INF/classes/目录下):
[html]
view plain
copy
print?
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
<context:component-scan base-package="com.liujan">
</context:component-scan>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/WEB-INF/page/" p:suffix=".jsp">
</bean>
</beans>
容器装配顺序造成的事务无法回滚
在加载这个web.xml这个文件时,Spring会去加载在这个文件中引入的其他文件,这里有mybatis-context.xml和application-context.xml这两个文件,两个文件都在/WebContent/WEB-INF/classes/目录下。
但是这里有个问题,就是这两个文件的加载是有先后顺序的。Spring会优先加载在context-param标签中引入的文件,也就是这里的mybatis-context.xml,然后再加载application-context.xml。在加载mybatis-context.xml时,Spring就会装配@Controlle和@Service的容器,并把他们放到Spring的容器中来;接着加载application-context.xml,这时Spring也会把@Service和@Controller注解的实例装配到Spring容器中,但是这时由于先前在加载mybatis-context.xml时已经装配过@Service和@Controller的容器,所以这时新装配的容器会覆盖掉先前的容器,所以Spring容器中就只剩下后来装配的容器了。
这种装配顺序就会引来一个问题,由于我的事务是在mybatis-context.xml文件中声明的,所以这个文件中的Service容器是带有事务能力的;但是这里装配的Service容器会被后来application-context.xml中的Service容器替换掉,而application-context.xml中的Service容器是没有事务能力的,所以最后造成在Spring容器中的Service是没有事务能力的。
这是很容易就陷入的一个陷阱;很多初学者在照着网上的教程配置好后,却发现无论如何都不能回滚或者其他事务的问题,很多时候就是由于这种情况造成的。
要解决这个问题,只需只在mybatis-context.xml中生成Service的带事务的容器,而在application-context.xml中就不生成Service容器了。又由于Service是Controller类中的一个属性,所以在装配Controller前要先装配好Service。
为了达到以上目的,由于是先加载myabtis-context.xml,后加载application-context.xml,所以我们只需在myabtis-context.xml装配Service,不装配Controller;然后在application-context.xml中装配Controller,不装配Service就可以了。这样就得修改mybatis-context.xml和application-context.xml这两个文件,修改后的文件
如下:
mybatis-context.xml:
[html]
view plain
copy
print?
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 不装配Controller -->
<context:component-scan base-package="com.liujan">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<bean id="config" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:db-config.properties</value>
</list>
</property>
</bean>
<!-- 配置数据库dataSource -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driverClass}" />
<property name="username" value="${jdbc.username}" />
<property name="url" value="${jdbc.url}" />
</bean>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="typeAliasesPackage" value="com.liujan.entity" />
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.liujan.mapper" />
</bean>
</beans>
application-context.xml
[html]
view plain
copy
print?
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
<!-- 装配Controller,不装配Service -->
<context:component-scan base-package="com.liujan">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service" />
</context:component-scan>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/WEB-INF/page/" p:suffix=".jsp">
</bean>
</beans>
web.xml内容不变。
2. 我们把事务控制在Service层,当一个函数(假设函数名为funA)前加上@Transactional标签时,就表示这个函数具有事务能力。那么什么情况下事务会commit,什么情况下事务会rollback?
如果要事务回滚,则funA函数内必须得抛出一个uncheck异常(比如RuntimeException),而且这个异常不能在funA内被try...catch,比如,在UserService类里有一个函数叫saveUser,是用来往数据库中插入一条数据的:
[java]
view plain
copy
print?
@Transactional(propagation=Propagation.REQUIRED)
public void saveUser(User u) throws Exception{
userMapper.insert(u);
throw new RuntimeException("hehe");
}
但是,如果这个异常被saveUser捕捉了,那么Spring就不会检测到这个异常,事务就不会回滚,数据就会插入成功,如下:
[java]
view plain
copy
print?
@Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class)
public void saveUser(User u) throws Exception{
userMapper.insert(u);
try {
throw new RuntimeException("hehe");
} catch (Exception e) {
// TODO: handle exception
}
}
但是,这个异常如果被调用saveUser的函数catch的话,事务也会回滚的,比如在UserController中有一个方法addUser,在这个方法里会调用userService的saveUser方法:
[java]
view plain
copy
print?
public void addUser(String name, int stuId) {
User u = new User();
u.setName(name);
u.setStuid(stuId);
try {
userService.saveUser(u);
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
综上所述,只要异常不在异常抛出的地方被catch,那么即使该异常在后面的函数中被catch,事务也会回滚。
这里又有一个问题,就是抛出的异常只能是uncheck类的,如果是check类的事务就不会回滚,如下:
[java]
view plain
copy
print?
@Transactional(propagation=Propagation.REQUIRED)
public void saveUser(User u) throws Exception{
userMapper.insert(u);
throw new Exception("hehe");
}
如果想在跑出check类异常时事务也会回滚,则可以在@Transactional中声明当遇上check类异常时回滚,如下:
[java]
view plain
copy
print?
@Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class)
public void saveUser(User u) throws Exception{
userMapper.insert(u);
throw new Exception("hehe");
}
最后讲下@Transactional标签中propagation的含义:
REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。
REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
NESTED:支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务。
顶 5 踩 0
上一篇maven ContextLoaderListener not found
下一篇mac命令行打开sublime
相关文章推荐
•
java解析excel并导入数据库的web工程
•
Python全栈工程师特训班--韦玮
•
聊天机器人2002
•
Blink在阿里集团的应用实践--陈守元
•
Delphi7高级应用开发随书源码
•
Vue2.x知识点面面通
•
Spring/SpringMVC/MyBatis整合+事务回滚
•
大型Web构架设计案例解析
•
java 利用POI 导入导出Excel
•
机器学习案例实战--欺诈检测
•
mavin spring4 mvc mybatis 整合 带事务,REST风格
•
Android开发实战30分钟集成第三方SDK
•
ssm+分页+支持导入导出excel文件
•
迅捷全站功能模块
•
封装了DAO对象用于直接操纵access数据库
•
springMVC+MYBATIS事务管理
查看评论
1楼 qq4769291622015-08-29 15:56发表
[回复] [引用][举报]
弱弱的问下 为啥要2个xml ,直接用默认的 mvc-dispatcher-servlet.xml 配置 所有bean 不行么?
Re: sysu安仔2015-09-01 15:58发表
[回复] [引用][举报]
回复qq476929162:也可以,这样只是为了将不同模块的配置分开了,以后方便管理。
发表评论
用 户 名:
xlnwrnmdbb
评论内容:
HTML/XMLobjective-cDelphiRubyPHPC#C++JavaScriptVisual
BasicPythonJavaCSSSQL其它
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
SpringMVC支持声明式和编程式事务管理,这里我讲的是声明式事务,也即通过注解@Transactional来使用事务。
这里是我在这个工程中所使用的jar包:http://download.csdn.net/detail/liujan511536/9050079
这里是我这个工程的源码:http://download.csdn.net/detail/liujan511536/9050093
这是一个dynamic web project。
首先看配置文件web.xml,这个文件在WebContent/WEB-INF/目录下:
[html]
view plain
copy
print?
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<display-name>SpringMVC_Mybatis</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<!-- 读取mybatis配置文件 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:mybatis-context.xml</param-value>
</context-param>
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<!-- 加载springmvc的xml到spring的上下文容器中 -->
<param-name>contextConfigLocation</param-name>
<param-value>
<!-- /WEB-INF/classes/application-context.xml -->
classpath:application-context.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>
</web-app>
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>SpringMVC_Mybatis</display-name> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> <!-- 读取mybatis配置文件 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:mybatis-context.xml</param-value> </context-param> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <!-- 加载springmvc的xml到spring的上下文容器中 --> <param-name>contextConfigLocation</param-name> <param-value> <!-- /WEB-INF/classes/application-context.xml --> classpath:application-context.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> </web-app>
mybatix-context.xml (/WebContent/WEB-INF/classes/目录下):
[html]
view plain
copy
print?
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="config" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:db-config.properties</value>
</list>
</property>
</bean>
<!-- 配置数据库dataSource -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driverClass}" />
<property name="username" value="${jdbc.username}" />
<property name="url" value="${jdbc.url}" />
</bean>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="typeAliasesPackage" value="com.liujan.entity" />
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.liujan.mapper" />
</bean>
</beans>
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="config" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <list> <value>classpath:db-config.properties</value> </list> </property> </bean> <!-- 配置数据库dataSource --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="${jdbc.driverClass}" /> <property name="username" value="${jdbc.username}" /> <property name="url" value="${jdbc.url}" /> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <tx:annotation-driven transaction-manager="transactionManager" /> <bea 16907 n id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="typeAliasesPackage" value="com.liujan.entity" /> </bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.liujan.mapper" /> </bean> </beans>
application-context.xml (/WebContent/WEB-INF/classes/目录下):
[html]
view plain
copy
print?
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
<context:component-scan base-package="com.liujan">
</context:component-scan>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/WEB-INF/page/" p:suffix=".jsp">
</bean>
</beans>
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"> <context:component-scan base-package="com.liujan"> </context:component-scan> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/WEB-INF/page/" p:suffix=".jsp"> </bean> </beans>
容器装配顺序造成的事务无法回滚
在加载这个web.xml这个文件时,Spring会去加载在这个文件中引入的其他文件,这里有mybatis-context.xml和application-context.xml这两个文件,两个文件都在/WebContent/WEB-INF/classes/目录下。
但是这里有个问题,就是这两个文件的加载是有先后顺序的。Spring会优先加载在context-param标签中引入的文件,也就是这里的mybatis-context.xml,然后再加载application-context.xml。在加载mybatis-context.xml时,Spring就会装配@Controlle和@Service的容器,并把他们放到Spring的容器中来;接着加载application-context.xml,这时Spring也会把@Service和@Controller注解的实例装配到Spring容器中,但是这时由于先前在加载mybatis-context.xml时已经装配过@Service和@Controller的容器,所以这时新装配的容器会覆盖掉先前的容器,所以Spring容器中就只剩下后来装配的容器了。
这种装配顺序就会引来一个问题,由于我的事务是在mybatis-context.xml文件中声明的,所以这个文件中的Service容器是带有事务能力的;但是这里装配的Service容器会被后来application-context.xml中的Service容器替换掉,而application-context.xml中的Service容器是没有事务能力的,所以最后造成在Spring容器中的Service是没有事务能力的。
这是很容易就陷入的一个陷阱;很多初学者在照着网上的教程配置好后,却发现无论如何都不能回滚或者其他事务的问题,很多时候就是由于这种情况造成的。
要解决这个问题,只需只在mybatis-context.xml中生成Service的带事务的容器,而在application-context.xml中就不生成Service容器了。又由于Service是Controller类中的一个属性,所以在装配Controller前要先装配好Service。
为了达到以上目的,由于是先加载myabtis-context.xml,后加载application-context.xml,所以我们只需在myabtis-context.xml装配Service,不装配Controller;然后在application-context.xml中装配Controller,不装配Service就可以了。这样就得修改mybatis-context.xml和application-context.xml这两个文件,修改后的文件
如下:
mybatis-context.xml:
[html]
view plain
copy
print?
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 不装配Controller -->
<context:component-scan base-package="com.liujan">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<bean id="config" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:db-config.properties</value>
</list>
</property>
</bean>
<!-- 配置数据库dataSource -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driverClass}" />
<property name="username" value="${jdbc.username}" />
<property name="url" value="${jdbc.url}" />
</bean>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="typeAliasesPackage" value="com.liujan.entity" />
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.liujan.mapper" />
</bean>
</beans>
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 不装配Controller --> <context:component-scan base-package="com.liujan"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> <bean id="config" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <list> <value>classpath:db-config.properties</value> </list> </property> </bean> <!-- 配置数据库dataSource --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="${jdbc.driverClass}" /> <property name="username" value="${jdbc.username}" /> <property name="url" value="${jdbc.url}" /> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <tx:annotation-driven transaction-manager="transactionManager" /> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="typeAliasesPackage" value="com.liujan.entity" /> </bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.liujan.mapper" /> </bean> </beans>
application-context.xml
[html]
view plain
copy
print?
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
<!-- 装配Controller,不装配Service -->
<context:component-scan base-package="com.liujan">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service" />
</context:component-scan>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/WEB-INF/page/" p:suffix=".jsp">
</bean>
</beans>
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"> <!-- 装配Controller,不装配Service --> <context:component-scan base-package="com.liujan"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service" /> </context:component-scan> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/WEB-INF/page/" p:suffix=".jsp"> </bean> </beans>
web.xml内容不变。
2. 我们把事务控制在Service层,当一个函数(假设函数名为funA)前加上@Transactional标签时,就表示这个函数具有事务能力。那么什么情况下事务会commit,什么情况下事务会rollback?
如果要事务回滚,则funA函数内必须得抛出一个uncheck异常(比如RuntimeException),而且这个异常不能在funA内被try...catch,比如,在UserService类里有一个函数叫saveUser,是用来往数据库中插入一条数据的:
[java]
view plain
copy
print?
@Transactional(propagation=Propagation.REQUIRED)
public void saveUser(User u) throws Exception{
userMapper.insert(u);
throw new RuntimeException("hehe");
}
@Transactional(propagation=Propagation.REQUIRED) public void saveUser(User u) throws Exception{ userMapper.insert(u); throw new RuntimeException("hehe"); }当运行这个函数时,是先往数据库中插入数据u,然后再抛出RuntimeException异常,而且这个异常没有被saveUser函数捕捉,所以这个异常会被Spring检测到,这时事务就会回滚,数据插入失败;
但是,如果这个异常被saveUser捕捉了,那么Spring就不会检测到这个异常,事务就不会回滚,数据就会插入成功,如下:
[java]
view plain
copy
print?
@Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class)
public void saveUser(User u) throws Exception{
userMapper.insert(u);
try {
throw new RuntimeException("hehe");
} catch (Exception e) {
// TODO: handle exception
}
}
@Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class) public void saveUser(User u) throws Exception{ userMapper.insert(u); try { throw new RuntimeException("hehe"); } catch (Exception e) { // TODO: handle exception } }
但是,这个异常如果被调用saveUser的函数catch的话,事务也会回滚的,比如在UserController中有一个方法addUser,在这个方法里会调用userService的saveUser方法:
[java]
view plain
copy
print?
public void addUser(String name, int stuId) {
User u = new User();
u.setName(name);
u.setStuid(stuId);
try {
userService.saveUser(u);
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
public void addUser(String name, int stuId) { User u = new User(); u.setName(name); u.setStuid(stuId); try { userService.saveUser(u); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } }这时事务也会回滚。
综上所述,只要异常不在异常抛出的地方被catch,那么即使该异常在后面的函数中被catch,事务也会回滚。
这里又有一个问题,就是抛出的异常只能是uncheck类的,如果是check类的事务就不会回滚,如下:
[java]
view plain
copy
print?
@Transactional(propagation=Propagation.REQUIRED)
public void saveUser(User u) throws Exception{
userMapper.insert(u);
throw new Exception("hehe");
}
@Transactional(propagation=Propagation.REQUIRED) public void saveUser(User u) throws Exception{ userMapper.insert(u); throw new Exception("hehe"); }这时事务是不会回滚的。
如果想在跑出check类异常时事务也会回滚,则可以在@Transactional中声明当遇上check类异常时回滚,如下:
[java]
view plain
copy
print?
@Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class)
public void saveUser(User u) throws Exception{
userMapper.insert(u);
throw new Exception("hehe");
}
@Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class) public void saveUser(User u) throws Exception{ userMapper.insert(u); throw new Exception("hehe"); }
最后讲下@Transactional标签中propagation的含义:
REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。
REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
NESTED:支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务。
顶 5 踩 0
上一篇maven ContextLoaderListener not found
下一篇mac命令行打开sublime
相关文章推荐
•
java解析excel并导入数据库的web工程
•
Python全栈工程师特训班--韦玮
•
聊天机器人2002
•
Blink在阿里集团的应用实践--陈守元
•
Delphi7高级应用开发随书源码
•
Vue2.x知识点面面通
•
Spring/SpringMVC/MyBatis整合+事务回滚
•
大型Web构架设计案例解析
•
java 利用POI 导入导出Excel
•
机器学习案例实战--欺诈检测
•
mavin spring4 mvc mybatis 整合 带事务,REST风格
•
Android开发实战30分钟集成第三方SDK
•
ssm+分页+支持导入导出excel文件
•
迅捷全站功能模块
•
封装了DAO对象用于直接操纵access数据库
•
springMVC+MYBATIS事务管理
查看评论
1楼 qq4769291622015-08-29 15:56发表
[回复] [引用][举报]
弱弱的问下 为啥要2个xml ,直接用默认的 mvc-dispatcher-servlet.xml 配置 所有bean 不行么?
Re: sysu安仔2015-09-01 15:58发表
[回复] [引用][举报]
回复qq476929162:也可以,这样只是为了将不同模块的配置分开了,以后方便管理。
发表评论
用 户 名:
xlnwrnmdbb
评论内容:
HTML/XMLobjective-cDelphiRubyPHPC#C++JavaScriptVisual
BasicPythonJavaCSSSQL其它
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
相关文章推荐
- SpringMVC+MyBatis 事务管理二
- SpringMVC+MyBatis 事务管理二
- Mybatis + SpringMVC事务管理
- SpringMVC+MyBatis 事务管理(实例)
- Mybatis + SpringMVC事务管理
- spring+springMvc+mybatis配置事务管理
- SpringMVC + Spring + MyBatis 学习笔记:SpringMVC和Spring一同工作的时候,AOP事务管理不起作用的解决方法
- Spring多数据源分布式事务管理/springmvc+spring+atomikos[jta]+druid+mybatis
- springmvc + mybatis 事务管理(全注解式)
- Spring多数据源分布式事务管理/springmvc+spring+atomikos[jta]+druid+mybatis
- SpringMVC+MyBatis 事务管理一
- spring + springmvc+ mybatis 事务管理及控制
- spring数据源配置及事务管理--mybatis3
- ssm公司员工管理系统(SpringMVC + Spring + MyBatis)
- springmvc+jdbc 声明式事务管理,适用于多种数据库框架
- mybatis3整合spring3的事务管理
- SpringMVC+MyBatis配置声明式事务的问题
- MyBatis+Spring 事务管理
- 基于spring mvc + spring+mybatis+easyui+jquery+maven+mysql的后台权限管理系统