事务就是多个数据库基本操作的集合体。集合后就是一个事务单元,事务操作就是,要么都成功,要么都失败。
要是对事务没有了解可以查看:
事务的介绍以及事务隔离级别
Spring事务管理两种方式
第一种:编程式事务管理(不用)
第二种:声明式事务管理。
声明式事务管理分为两种:
1.基于xml配置文件实现
2.基于注解实现
Spring事务管理高层抽象主要包括3个接口
PlatformTransactionManager 事务管理器
TransactionDefinition 事务定义信息(隔离、传播、超时、只读)
TransactionStatus 事务具体运行状态
我们这里使用PlatformTransactionManager(事务管理器)
Spring为不同的持久化框架提供了不同PlatformTransactionManager(事务管理器)接口实现
事务 | 说明 |
---|---|
org.springframework.jdbc.datasource.DataSourceTransactionManager | 使用Spring JDBC或 IBatis 进行持久化时间时使用 |
org.springframework.orm.hibernate5.HibernateTransactionManager | 使用Hibernate5.0版本进行持久化数据时使用 |
org.springframework.orm.jpa.JpaTransactionManager | 使用JPA进行持久化时使用 |
org.springframework.jdo.JdoTransactionManager | 当持久化机制是Jdo时使用 |
org.springframework.transaction.ita.ItaTransactionManager | 使用一个JTA实现来管理事务,在一个事务跨越多个资源时必须使用 |
不管是使用xml配置文件实现还是基于xml注解实现,都要首先配置事务管理器
下面来个需求:账户转账
首先搭建转账环境:用c3p0和JdbcTemplate实现基本的转账功能
创建数据库表,添加数据:
create table zhanghu(
id int primary key,
username char(20),
salary int);
insert into zhanghu values(1,'FireLang',10000);
insert into zhanghu values(2,'LangShen',12300);
创建Service和Dao类完成注入:
1.Service层也被叫做业务逻辑层。
2.dao层,单纯对数据库操作层,在dao层不添加业务。这些都是一种规范。
这是dao层:
@Component(value="moneyDao")
public class MoneyDao {
@Resource(name="jdbcTemplate")
private JdbcTemplate jdbcTemplate;
public void reduceMoney(String name,int reduceNum){
String sql="update zhanghu set salary=salary-? where username=?";
jdbcTemplate.update(sql, reduceNum,name);
}
public void addMoney(String name,int addNum){
String sql="update zhanghu set salary=salary+? where username=?";
jdbcTemplate.update(sql, addNum,name);
}
}
这是Service层:
@Service(value="moneyService")
public class MoneyService {
@Resource(name="moneyDao")
private MoneyDao moneyDao;
public void transferAccounts(String reduceName,String addName,int num){
moneyDao.reduceMoney(reduceName, num);
moneyDao.addMoney(addName, num);
}
}
这是Spring核心配置文件的配置:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
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/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="cn.domarvel">context:component-scan>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver">property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/template">property>
<property name="user" value="root">property>
<property name="password" value="">property>
bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg name="dataSource" ref="dataSource">constructor-arg>
bean>
beans>
测试代码:
public class MoneyServiceTest {
@Test
public void transferAccounts(){
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
((MoneyService)context.getBean("moneyService")).transferAccounts("LangShen", "FireLang", 1000);
}
}
问题出现:
在这里我们已经成功的完成了转账,但是如果在Service层的转账方法里面,在转账过程中出现了转账的异常,比如我现在给发起方扣除了钱,但是现在突然现在机房断电了,后续的加钱操作也就没有了。出现了转账错误,钱凭空消失的问题,给用户会造成很大的困扰。
解决方法:
所以到了这里就需要事务的操作了。在事务操作中,如果我们在扣除钱后,加钱之前出现了突然断电情况,那么当数据库再次开启后,这个事务是会回滚的,因为事务的特性就是,在事务里面的操作,要么全部成功,要么全部都不成功,不成功的是会事务回滚,回滚到操作之前。其中回滚是按照数据库日志回滚的。
下面我们就用到Spring的事务操作:
我们用到了Spring的声明式事务管理:
两种方式都会讲到:
1.基于xml配置文件实现
2.基于注解实现
要用Spring的事务,就得导包,以及在Spring核心配置文件中修改约束:
对于导包,你就导入JdbcTemplate包和spring-tx包就行了。 下载地址
这里用到的连接池是c3p0 下载地址
MySQL驱动包5.1.13下载
因为在Spring的事务操作中会用到aop思想,所以我们还要导入AspectJ包,以及Spring对AspectJ的依赖包。
下载地址
约束我已经整理好了:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" 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/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
配置文件方式使用aop思想配置
第一步: 配置事务管理器(在核心Spring配置文件中进行配置)
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource">property>
bean>
第二步: 配置事务增强
<tx:advice id="txadvice" transaction-manager="dataSourceTransactionManager">
<tx:attributes>
<tx:method name="transfer*" propagation="REQUIRED"/>
tx:attributes>
tx:advice>
第三步: 配置切面
<aop:config>
<aop:pointcut expression="execution(* cn.domarvel.service.MoneyService.transferAccounts(..))" id="pointcut1"/>
<aop:advisor advice-ref="txadvice" pointcut-ref="pointcut1"/>
aop:config>
整体代码:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" 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/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:component-scan base-package="cn.domarvel">context:component-scan>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver">property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/template">property>
<property name="user" value="root">property>
<property name="password" value="">property>
bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg name="dataSource" ref="dataSource">constructor-arg>
bean>
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource">property>
bean>
<tx:advice id="txadvice" transaction-manager="dataSourceTransactionManager">
<tx:attributes>
<tx:method name="transfer*" propagation="REQUIRED"/>
tx:attributes>
tx:advice>
<aop:config>
<aop:pointcut expression="execution(* cn.domarvel.service.MoneyService.*(..))" id="pointcut1"/>
<aop:advisor advice-ref="txadvice" pointcut-ref="pointcut1"/>
aop:config>
beans>
在这里面还有几个常用属性:
method name="transfer*" propagation="REQUIRED"/>
比如:rollback-for,propagation(事务传播级别)
read-only(true-false):
还有一个常用的属性isolation(事务隔离级别)
Spring事务的隔离级别
注意Spring核心配置文件没有先后配置顺序。
好了现在事务设置好了,我们要准备出现异常了:
@Service(value="moneyService")
public class MoneyService {
@Resource(name="moneyDao")
private MoneyDao moneyDao;
public void transferAccounts(String reduceName,String addName,int num){
moneyDao.reduceMoney(reduceName, num);
int a=10/0;//在把钱减少后,给别人添加钱之前出现了异常,那么事务将会回滚,而不会单方面的少钱或者多钱。
moneyDao.addMoney(addName, num);
}
}
运行测试代码操作后,将不会单方面少钱或者多钱。
第一步:配置事务管理器
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource">property>
bean>
第二步:配置事务注解
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
整体Spring的核心配置:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" 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/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:component-scan base-package="cn.domarvel">context:component-scan>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver">property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/template">property>
<property name="user" value="root">property>
<property name="password" value="">property>
bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg name="dataSource" ref="dataSource">constructor-arg>
bean>
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource">property>
bean>
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
beans>
第三步:在要使用事务的方法所在类上面添加注解
@Service(value="moneyService")
@Transactional//在当前操作类上配置事务管理。默认就是处理该类中的所有方法。
public class MoneyService {
@Resource(name="moneyDao")
private MoneyDao moneyDao;
public void transferAccounts(String reduceName,String addName,int num){
moneyDao.reduceMoney(reduceName, num);
int a=10/0;//在把钱减少后,给别人添加钱之前出现了异常,那么事务将会回滚,而不会单方面的少钱或者多钱。
moneyDao.addMoney(addName, num);
}
}
如果想要某一个方法不加入事务处理,只需要像下面这样:
@Service(value="moneyService")
@Transactional
public class MoneyService {
@Resource(name="moneyDao")
private MoneyDao moneyDao;
@Transactional(propagation = Propagation.NOT_SUPPORTED)//让当前方法不加入事务处理
public void transferAccounts(String reduceName,String addName,int num){
moneyDao.reduceMoney(reduceName, num);
int a=10/0;//那么没有事务处理的话,就会单方面的少钱或者多钱,比如这里就会单方面少钱。
moneyDao.addMoney(addName, num);
}
}
上面类中事务的处理行为有个专业名词叫做事务的传播行为。
事务传播行为介绍:
@Transactional(propagation=Propagation.REQUIRED)
如果有事务, 那么加入事务, 没有的话新建一个(默认情况下)
@Transactional(propagation=Propagation.NOT_SUPPORTED)
容器不为这个方法开启事务
@Transactional(propagation=Propagation.REQUIRES_NEW)
不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
@Transactional(propagation=Propagation.MANDATORY)
必须在一个已有的事务中执行,否则抛出异常
@Transactional(propagation=Propagation.NEVER)
必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反)
@Transactional(propagation=Propagation.SUPPORTS)
如果其他bean调用这个方法,在其他bean中声明事务,那就用事务.如果其他bean没有声明事务,那就不用事务.
事务超时设置:
@Transactional(timeout=30) //默认是30秒
事务传播可以参考:
MySQL隔离级别和事务传播(隔离级别这个作者有些地方讲错了,但是事务传播讲的还不错,主要是图不错)