一、准备工作:
模拟买书的场景:
1.输入书名买书
2.账户余额减五
3.书库数量减少一
当书库数量不足时,如果不存在事务的话,则书库数量不变,但账户余额却减少了
二、配置声明式事务(注解的方式)
在application.xml中配置:
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 启动事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
三、在编写买书方法:
/**
* 事务的传播行为:一个事务方法被另一个事务方法调用,
* 被调用的事务方法如何工作在事务中
* 使用propagation 指定事务的传播行为,即当前的事务被另一个事务方法调用时
*/
@Transactional(propagation=Propagation.REQUIRED)
@Override
public void buy(String name) {
System.out.println("购书开始");
int money = Integer.parseInt(selectCount(name))-5;
if(money <0){
throw new MyException("账户余额不足");
}
//更新账户余额
updateCount(name);
int num = Integer.parseInt(selectBookNum(name))-1;
if(num <0){
throw new MyException("库存不足");
}
//更新书库数量
updateBook(name);
System.out.println("购书完成");
}
MyException 为自定义的异常集成RuntimeException,
因为spring声明式事务对运行时异常支持事务的回滚
四:事务的传播行为:
事务的7种传播行为: 默认为REQUIRED
PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
PROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务,
则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行
五、编写买多本书的方法
/**
* 买多本书
*/
@Transactional
@Override
public void buyButch(String name, int num) {
for(int i=0;i<num;i++){
//调用买单本书的方法
testService.buy(name);
}
}
当将buy 方法的事务注解改为:
@Transactional(propagation=Propagation.REQUIRED_NEW) 时,
买多本书的时候,当库存不足的时候之前的不回滚
@Transactional(propagation=Propagation.REQUIRED) 时,
买多本书的时候,当库存不足的时候之前的回滚
六:事务的隔离级别: 事务并发的时候,可能存在脏读,幻读,不可重复读
Isolation 属性一共支持五种事务设置,具体介绍如下:
使用isolation 来指定事务的隔离级别
常用:READ_COMMITTED 读与提交
DEFAULT 使用数据库设置的隔离级别 ( 默认 ) ,由 DBA 默认的设置来决定隔离级别 .
READ_UNCOMMITTED 会出现脏读、不可重复读、幻读 ( 隔离级别最低,并发性能高 )
READ_COMMITTED 会出现不可重复读、幻读问题(锁定正在读取的行)
REPEATABLE_READ 会出幻读(锁定所读取的所有行)
SERIALIZABLE 保证所有的情况不会发生(锁表)
七、异常回滚
默认情况下spring 的声明式事务对所有的运行时异常进行回滚,也可以通过对已的属性进行设置
noRollbackFor 对异常别回滚了
@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.READ_COMMITTED
,noRollbackFor={MyException.class},readOnly=false,timeout=3)
rollbackFor 可以指定异常进行回滚
readOnly=false 设置只读属性,表示这个事务只读取数据,但不能更新数据
这样可以帮助数据库优化引擎,若真的是一个只读数据库值的方法,应设置readOnly = true
timeout 指定强制回滚之前,事务可以占用的时间,防止事务对数据库链接占用时间过长
八、基于XML文件的形式配置事务
修改XML文件:
<!-- 基于XML的形式配置事务 -->
<!-- 1.配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 2.配置事务属性 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 针对某名称的方法,配置传播行为属性-->
<tx:method name="buy" propagation="REQUIRES_NEW"></tx:method>
<!-- 针对所有的方法 -->
<tx:method name="*"></tx:method>
<!-- 针对get方法开头的为只读 -->
<tx:method name="get*" read-only="true"></tx:method>
</tx:attributes>
</tx:advice>
<!-- 3.配置事务切点 ,以及把事务切入点及事务属性关联起来: 作用在哪些类的哪些方法上-->
<aop:config>
<aop:pointcut expression="execution(* service.impl.*.*(..))" id="txPointCut"></aop:pointcut>
<!-- 切点和事务关联 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"></aop:advisor>
</aop:config>
模拟买书的场景:
1.输入书名买书
2.账户余额减五
3.书库数量减少一
当书库数量不足时,如果不存在事务的话,则书库数量不变,但账户余额却减少了
二、配置声明式事务(注解的方式)
在application.xml中配置:
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 启动事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
三、在编写买书方法:
/**
* 事务的传播行为:一个事务方法被另一个事务方法调用,
* 被调用的事务方法如何工作在事务中
* 使用propagation 指定事务的传播行为,即当前的事务被另一个事务方法调用时
*/
@Transactional(propagation=Propagation.REQUIRED)
@Override
public void buy(String name) {
System.out.println("购书开始");
int money = Integer.parseInt(selectCount(name))-5;
if(money <0){
throw new MyException("账户余额不足");
}
//更新账户余额
updateCount(name);
int num = Integer.parseInt(selectBookNum(name))-1;
if(num <0){
throw new MyException("库存不足");
}
//更新书库数量
updateBook(name);
System.out.println("购书完成");
}
MyException 为自定义的异常集成RuntimeException,
因为spring声明式事务对运行时异常支持事务的回滚
四:事务的传播行为:
事务的7种传播行为: 默认为REQUIRED
PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
PROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务,
则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行
五、编写买多本书的方法
/**
* 买多本书
*/
@Transactional
@Override
public void buyButch(String name, int num) {
for(int i=0;i<num;i++){
//调用买单本书的方法
testService.buy(name);
}
}
当将buy 方法的事务注解改为:
@Transactional(propagation=Propagation.REQUIRED_NEW) 时,
买多本书的时候,当库存不足的时候之前的不回滚
@Transactional(propagation=Propagation.REQUIRED) 时,
买多本书的时候,当库存不足的时候之前的回滚
六:事务的隔离级别: 事务并发的时候,可能存在脏读,幻读,不可重复读
Isolation 属性一共支持五种事务设置,具体介绍如下:
使用isolation 来指定事务的隔离级别
常用:READ_COMMITTED 读与提交
DEFAULT 使用数据库设置的隔离级别 ( 默认 ) ,由 DBA 默认的设置来决定隔离级别 .
READ_UNCOMMITTED 会出现脏读、不可重复读、幻读 ( 隔离级别最低,并发性能高 )
READ_COMMITTED 会出现不可重复读、幻读问题(锁定正在读取的行)
REPEATABLE_READ 会出幻读(锁定所读取的所有行)
SERIALIZABLE 保证所有的情况不会发生(锁表)
七、异常回滚
默认情况下spring 的声明式事务对所有的运行时异常进行回滚,也可以通过对已的属性进行设置
noRollbackFor 对异常别回滚了
@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.READ_COMMITTED
,noRollbackFor={MyException.class},readOnly=false,timeout=3)
rollbackFor 可以指定异常进行回滚
readOnly=false 设置只读属性,表示这个事务只读取数据,但不能更新数据
这样可以帮助数据库优化引擎,若真的是一个只读数据库值的方法,应设置readOnly = true
timeout 指定强制回滚之前,事务可以占用的时间,防止事务对数据库链接占用时间过长
八、基于XML文件的形式配置事务
修改XML文件:
<!-- 基于XML的形式配置事务 -->
<!-- 1.配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 2.配置事务属性 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 针对某名称的方法,配置传播行为属性-->
<tx:method name="buy" propagation="REQUIRES_NEW"></tx:method>
<!-- 针对所有的方法 -->
<tx:method name="*"></tx:method>
<!-- 针对get方法开头的为只读 -->
<tx:method name="get*" read-only="true"></tx:method>
</tx:attributes>
</tx:advice>
<!-- 3.配置事务切点 ,以及把事务切入点及事务属性关联起来: 作用在哪些类的哪些方法上-->
<aop:config>
<aop:pointcut expression="execution(* service.impl.*.*(..))" id="txPointCut"></aop:pointcut>
<!-- 切点和事务关联 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"></aop:advisor>
</aop:config>