一、什么是事务:
我理解为:事务其实就是一件事情,要做就做完,中途出问题了就要恢复成原状态(等于没有做).
二、事务的四大特性
(1)、原子性:
事务必须是一个不可分割的整体
(2)、一致性:
执行完数据库操作后,数据不会被破坏。打个比方,如果从 A 账户转账到 B 账户,不可能因为 A 账户扣了钱,而 B 账户没有加钱吧
(3)、隔离性:
对同一条数据的操作,不同的事务之间应该隔离(提交之前,数据变化仅仅在事务内部,与外部隔离),要不然会产生脏读、幻读等情况情况。
为了让事务之间没有干扰,
制定了以下几种事务隔离级别:
<1>、READ_UNCOMMITTED
<2>、READ_COMMITTED
<3>、REPEATABLE_READ
<4>、SERIALIZABLE
从上往下,级别越来越高,并发性越来越差,安全性越来越高
(4)、持久性:
当我们执行一条 insert 语句后,数据库必须要保证有一条数据永久地存放在磁盘中,这个也算事务的一条特性
(5)、总结:
原子性是基础,隔离性是手段,持久性是目的,真正的老大就是一致性。数据不一致了就要乱套了。
三、JDBC不同事务隔离级别要解决的问题:
(1) :脏读:就是当前事务中访问到了其它事务还没有提交的数据,并且在此基础上做了其它的数据操作。
(2):不可重复读:我理解为当前事务中读取到了其它事务已经提交修改的数据(其它事务修改前读取了一次
,修改后又读取了一次,导致同一事务中两次读取的数据不一样)
(3):幻读:我理解为当前事务中读取到了其它事务提交新增的数据集(其它事务新增前读取了一次,
新增后又读取了一次,导致同一事务中读取到的结果集行数不同)
以上3种情况脏读是一定要避免的。
MySQL 数据库的默认事务隔离级别就是 READ_COMMITTED(只禁止脏读,也就是隔离其它事务没有提交的数据),READ_COMMITTED已经可以解决大部分的问题。
四、完善事务管理功能
(1)、事务传播行为
Spring 解决的只是方法之间的事务传播,比如A方法是一个事务,B方法是一个事务,A方法里面包含B方法。那么事务是怎么传播的?
(2)、PROPAGATION_REQUIRED:
最常用也是spring默认是事务传播级别,A方法执行的时候已经有事务了,所以B方法的事务会注入到A方法事务里面来(合成一个事务)。
(3)、Spring关于事务的附加功能
1、事务超时(Transaction Timeout):为了解决事务时间太长,消耗太多的资源,所以故意给事务设置一个最大时常,
如果超过了,就回滚事务。
------ timeout="1"
2、只读事务(Readonly Transaction):为了忽略那些不需要事务的方法,比如读取数据,这样可以有效地提高一些性能
------ propagation="REQUIRED"
事务最重要的两个特性,是事务的传播级别和数据隔离级别。传播级别定义的是事务的控制范围,事务隔离级别定义的是事务在数据库读写方面的控制范围。
五、如何配置Spring的事务管理:
(1)、在Spring配置文件中使用AOP的方式实现事务的配置:
<!-- 定义事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 定义通知 -->
<tx:advice id="TestAdvice" transaction-manager="transactionManager">
<!--配置事务传播性,隔离级别以及超时回滚等问题 -->
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED" isolation="DEFAULT" timeout="1"/>
<tx:method name="del*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="add*" propagation="REQUIRED" />
<tx:method name="*" rollback-for="Exception" />
</tx:attributes>
</tx:advice>
<aop:config>
<!--配置事务切点,将TestAdvice通知切入到com.website.service子目录下的所有方法 -->
<aop:pointcut id="services"
expression="execution(* com.website.service.*.*(..))" />
<aop:advisor pointcut-ref="services" advice-ref="TestAdvice" />
</aop:config>
(2)、注解式事务:
<1>、首先在Spring配置文件中设置事务管理器,定义通知tx:annotation-driven
<!-- 定义事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!--使用事务注解, 注解方式配置事务-->
<tx:annotation-driven transaction-manager="transactionManager" />
<2>、代码中的具体的注解以及事务的传播性、隔离级别一般在service 层中配置下面
@Transactional(propagation=Propagation.REQUIRED,rollbackFor=Exception.class,timeout=1,isolation=Isolation.DEFAULT)
public void saveUser(Map<String, String> map) throws Exception {
System.out.println("方法开始");
for (int i = 0; i < 500000; i++) {
System.out.println("*");
}
System.out.println("进入保存");
userDao.saveUser(map);
System.out.println("退出保存");
}
@Transactional 一般是放在public方法上面,也可以放在类上面,方法和类同时有@Transactional 注解时,方法上面的注解优先级高。
六、以上面的笔记来理解,Spring AOP是怎样来管理事务的.
1、配置文件里面用aop的方式切入事务到service的指定方法,每个service方法都被事务管理。事务的隔离级别是READ_COMMITTED(只禁止脏数据),事务的传播级别是PROPAGATION_REQUIRED(遇到新的事务,会将其加入到当前事务,合并成一个事务)
2、这个时候需要实现一个功能,先插入数据,再查询数据,有三种写法:
<1>、action层里面调用两个server方法:
@Action(value="getList")
public void getList(){
this.Service.add(); //添加方法
this.Service.getList(); //查询方法
}
分析:因为每个server是一个独立的事务,这里的action里面调用方法是按顺序执行的,
所以是先执行添加事务并且提交事务后再执行查询事务,结果是可以查到刚添加的数据
<2>、action层里面调用一个server方法,这个server方法里面调用添加和查询server方法
@Action(value="getList")
public void getList(){
this.Service.serviceFun();
}
server方法层:
public void serviceFun(){
this.add();
this.getList();
}
分析:因为事务的传播级别是PROPAGATION_REQUIRED(遇到新的事务,会将其加入到当前事务,合并成一个事务),所以this.add()和this.getList();是在一个事务内执行的,不存在什么添加事务提交不提交,都在一个隔离墙内,所以结果是可以查到刚添加的数据。
<3>、如果添加删除操作都在一个servier里面之间调用dao层方法,本来就是一个事务,那就更加可以查到刚添加的数据了。
以上是我对于spring Aop切入事务后,为什么我们不用考虑事务管理是原因的理解,因为spring都帮我们搞定了。