什么是事务?
指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。 简单的说,事务就是并发控制的单位,是用户定义的一个操作序列。
事物的特性?
- A:原子性(Atomicity) 事务中的操作要么都不做,要么就全做。
- C:一致性(Consistency) 事务执行的结果必须是从数据库从一个一致性状态转换到另一个一致性状态。
- I:隔离性(Isolation) 一个事务的执行不能被其他事务干扰
- D:持久性(Durability) 一个事务一旦提交,它对数据库中数据的改变就应该是永久性的
事务的分类
1.编程式事务
所谓编程式事务指的是通过编码方式实现事务,允许用户在代码中精确定义事务的边界。即类似于JDBC编程实现事务管理。管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,spring推荐使用TransactionTemplate。
2. 声明式事务
管理建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于@Transactional注解的方式),便可以将事务规则应用到业务逻辑中。
Spring中事务管理的核心接口
PlatformTransactionManager 事务管理器
Spring并不直接管理事务,通过这个接口,Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器,也就是将事务管理的职责委托给Hibernate或者JTA等持久化机制所提供的相关平台框架的事务来实现。
该接口中只有定义三个方法
1. getTransaction(TransactionDefinition definition) ,事务管理器 通过TransactionDefinition,获得“事务状态”,从而管理事务。
2. void commit(TransactionStatus status) 根据状态提交
3. void rollback(TransactionStatus status) 根据状态回滚
TransactionStatus 事务状态
这个接口描述的是一些处理事务提供简单的控制事务执行和查询事务状态的方法,在回滚或提交的时候需要应用对应的事务状态。
TransactionDefinition 基本事务属性的定义
这个方法里面的参数是TransactionDefinition类,这个类就定义了一些基本的事务属性。
那么什么是事务属性呢?事务属性可以理解成事务的一些基本配置,描述了事务策略如何应用到方法上。事务属性包含了5个方面,如图所示:
TransactionDefinition 接口方法如下:
1. 传播行为:当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。
Spring 定义了如下七中传播行为,这里以A业务和B业务之间如何传播事务为例说明:
①、PROPAGATION_REQUIRED :required , 必须。默认值,A如果有事务,B将使用该事务;如果A没有事务,B将创建一个新的事务。
②、PROPAGATION_SUPPORTS:supports ,支持。A如果有事务,B将使用该事务;如果A没有事务,B将以非事务执行。
③、PROPAGATION_MANDATORY:mandatory ,强制。A如果有事务,B将使用该事务;如果A没有事务,B将抛异常。
④、PROPAGATION_REQUIRES_NEW :requires_new,必须新的。如果A有事务,将A的事务挂起,B创建一个新的事务;如果A没有事务,B创建一个新的事务。
⑤、PROPAGATION_NOT_SUPPORTED :not_supported ,不支持。如果A有事务,将A的事务挂起,B将以非事务执行;如果A没有事务,B将以非事务执行。
⑥、PROPAGATION_NEVER :never,从不。如果A有事务,B将抛异常;如果A没有事务,B将以非事务执行。
⑦、PROPAGATION_NESTED :nested ,嵌套。A和B底层采用保存点机制,形成嵌套事务。
2. 隔离级别:定义了一个事务可能受其他并发事务影响的程度。
并发事务引起的问题:
在典型的应用程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务。并发虽然是必须的,但可能会导致以下的问题。
①、脏读(Dirty reads)——脏读发生在一个事务读取了另一个事务改写但尚未提交的数据时。如果改写在稍后被回滚了,那么第一个事务获取的数据就是无效的。
②、不可重复读(Nonrepeatable read)——不可重复读发生在一个事务执行相同的查询两次或两次以上,但是每次都得到不同的数据时。这通常是因为另一个并发事务在两次查询期间进行了更新。
③、幻读(Phantom read)——幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录。
注意:不可重复读重点是修改,而幻读重点是新增或删除。
在 Spring 事务管理中,为我们定义了如下的隔离级别:
①、ISOLATION_DEFAULT:使用后端数据库默认的隔离级别
②、ISOLATION_READ_UNCOMMITTED:最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
③、ISOLATION_READ_COMMITTED:允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
④、ISOLATION_REPEATABLE_READ:对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生
⑤、ISOLATION_SERIALIZABLE:最高的隔离级别,完全服从ACID的隔离级别,确保阻止脏读、不可重复读以及幻读,也是最慢的事务隔离级别,因为它通常是通过完全锁定事务相关的数据库表来实现的
3. 只读
这是事务的第三个特性,是否为只读事务。如果事务只对后端的数据库进行该操作,数据库可以利用事务的只读特性来进行一些特定的优化。通过将事务设置为只读,你就可以给数据库一个机会,让它应用它认为合适的优化措施。
4. 事务超时
为了使应用程序很好地运行,事务不能运行太长的时间。因为事务可能涉及对后端数据库的锁定,所以长时间的事务会不必要的占用数据库资源。事务超时就是事务的一个定时器,在特定时间内事务如果没有执行完毕,那么就会自动回滚,而不是一直等待其结束。
5. 回滚规则
事务五边形的最后一个方面是一组规则,这些规则定义了哪些异常会导致事务回滚而哪些不会。默认情况下,事务只有遇到运行期异常时才会回滚,而在遇到检查型异常时不会回滚(这一行为与EJB的回滚行为是一致的) 。但是你可以声明事务在遇到特定的检查型异常时像遇到运行期异常那样回滚。同样,你还可以声明事务遇到特定的异常不回滚,即使这些异常是运行期异常。
以上引用自https://www.cnblogs.com/ysocean/p/7617620.html
Spring中的事务管理
Spring中事务的配置
- 导入jar包(spring-tx.jar)
- 在配置文件中配置事务
<!-- 注册c3p0数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 注册事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 注册事务通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes >
<!-- isolation:事务隔离级别 propagation:事务传播行为-->
<tx:method name="open*" propagation="REQUIRED" isolation="DEFAULT" />
<tx:method name="buy*" propagation="REQUIRED" isolation="DEFAULT" rollback-for="BuyFundException"/>
</tx:attributes>
</tx:advice>
<!-- aop配置 -->
<aop:config>
<aop:pointcut expression="execution(* *..service.*.buyFund(..))" id="buyFund"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="buyFund"/>
</aop:config>
tx:method标签中的属性:
- name:方法名、可以使用通配符(*)
- isolation:事务隔离级别
- propagation:事务传播行为
- rollback-for:对于什么异常回滚
- no-rollback-for:对于异常不回滚
- timeout:设置超时时限
- read-only:设置是否只读、会对事务进行优化。
注解式事务管理开发
在方法前加上@Transactional注解,其属性和配置文件中属性配置一致,
@Transactional(rollbackFor=BuyFundException.class)
public void buyFund(int aid, double payMoney, int fid, int count) throws BuyFundException {
accountDaoImpl.updateAccount(aid, payMoney);
if(1==1) {
throw new BuyFundException("异常");
}
fundDaoImpl.updateFund(fid, count);
}
需要在配置文件中添加注解的扫描驱动
<!-- 事务注解驱动 -->
<tx:annotation-driven transaction-manager="transactionManager"/>