数据库事务的四大特性、隔离级别以及Spring中如何利用AOP进行事务管理

来源:互联网 发布:用二分法求根C语言 编辑:程序博客网 时间:2024/05/10 07:34

首先,什么是事务?

事务就是业务上的一个逻辑单元,它能够保证其中对数据所有的操作,要么成功,要么失败。

其次,事务的特性有哪些?

1.原子性。

例如,转账,A账户减少,B账户增加。虽然是两条 DML语句,但是被当做是一个整体,一次事务。两条语句只能同时成功或者同时失败。

2.一致性。

账户A和B,要么都是转账前的状态,要么都是转账后的状态。(不能A账户的钱减少了但是B账户的钱没有增加)。

3.隔离性。

虽然在某个时间段很多人都在转账,但是每个人的转账都是在一个自己的事务中,彼此不会影响。

4.持久性。

事务提交成功后,数据修改永远生效。

在考虑事务的隔离级别之前,需要认识到如果不考虑事务的隔离性,会发生的异常情况:

1.脏读

一个事务读取了另外一个事务未提交的数据。(会对系统的并发处理带来很大的隐患)

2.不可重复读

在同一个事务内,多次读同一个数据时,发现该数据已经 被另一个已经提交的事务修改。(在一个事务内两次读到的数据时是不一样的。)

3.幻读

一个事务根据相同的查询条件,重新执行查询,返回的记录中包含与前一次执行查询返回的记录不同的行。


以上这三种 情况都是同时进行的几个事务对相同的数据进行读取时造成的。

如何处理这几种异常呢?

ANSI SQL-92标准中定义了以下几种事务隔离级别:

1.Read Uncommitted

最低等级的事务隔离,仅仅保证读取过程中不会读到非法数据。(三种异常情况均可能发生)

2.Read Committed

避免了“脏读”。一个select查询只能查看到查询开始之前提交的数据。在查询执行时,其他事务修改或者提交的数据它看不到(数据库默认的事务隔离级别)

3.Repeatable Read

避免了“脏读”和“不可重复读”。一个事务不可能更新由另一个事务读取但是没有提交的数据。应用并不广泛。因为它可能出现幻读。同时带来更多性能损失。

4.Serializable

ORACLE数据库只支持Read Committed 和Serializable两种隔离级别。另外自定义了一个Read Only的隔离级别,只允许读,不允许改。避免不可重复读和幻读

三种都能避免。所有事务串行,而不是并行。

Spring事务的隔离级别
 1. ISOLATION_DEFAULT: 这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别.
      另外四个与JDBC的隔离级别相对应
 2. ISOLATION_READ_UNCOMMITTED: 这是事务最低的隔离级别,它充许令外一个事务可以看到这个事务未提交的数据。
      这种隔离级别会产生脏读,不可重复读和幻像读。
 3. ISOLATION_READ_COMMITTED: 保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据
 4. ISOLATION_REPEATABLE_READ: 这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。
      它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。
 5. ISOLATION_SERIALIZABLE 这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。
      除了防止脏读,不可重复读外,还避免了幻像读。

使用Spring AOP实现声明式事务管理

1.基于XML配置(使用较多)

(1)配置事务管理类

 <!-- 定义事务管理器 -->  <bean id="transactionManager"      class="org.springframework.jdbc.datasource.DataSourceTransactionManager">      <property name="dataSource" ref="dataSource" />  </bean>  
在spring的配置中配置数据源(dataSource)、事务管理器,事务管理器使用不同的orm框架事务管理器类就不同,mybatis 是org.springframework.jdbc.datasource.DataSourceTransactionManager  。而hibernate事务管理器为org.springframework.orm.hibernate3.HibernateTransactionManager  

(2)配置事务属性

<!-- 配置事务的属性 -->      <tx:advice id="TestAdvice" transaction-manager="transactionManager">          <!--配置事务传播性,隔离级别以及超时回滚等问题 -->          <tx:attributes>              <tx:method name="search*" propagation="REQUIRED" read-only="true" isolation="DEFAUT" TIMEOUT="-1" />              <tx:method name="del*" propagation="REQUIRED" />              <tx:method name="update*" propagation="REQUIRED" />              <tx:method name="add*" propagation="REQUIRED" />          </tx:attributes>      </tx:advice>

事务属性在<tx:method>中进行设置,Spring支持对不同的方法设置不同的事务属性,所以可以为一个<tx:advice>设置多个<tx:method>,其中name属性指定匹配的方法(这里需要对这些方法名进行约定,如果事务切入点在service上,则最好和Dao的方法命名区分开,也不要使用get set关键字,防止和属性的getter setter发生混淆)

事务有以下几个常用属性:

a.read-only:设置该事务中是否允许修改数据。(对于只执行查询功能的事务,设置为TRUE可以提高事务的执行速度)  

b.propagation:事务的传播机制。一般设置为required。可以保证在事务中的代码只在当前事务中运行,防止创建多个事务。

c.isolation:事务隔离级别。不是必须的。默认值是default

d.timeout:允许事务运行的最长时间,以秒为单位。

e.rollback-for:触发回滚的异常。

f.no-rollback-for:不会触发回滚的异常。

***实际开发中,对于只执行查询功能的事务,要设置read-onlyTRUE,其他属性一般使用默认值即可。


(3)配置事务的AOP切入点

<aop:config>          <!--配置事务切点 -->          <aop:pointcut id="services"              expression="execution(public* com.pb.service.*.*(..))" />          <aop:advisor pointcut-ref="services" advice-ref="TestAdvice" />      </aop:config>  

该设置的含义是:对于com.pb.service.impl包及子包下的所有类的所有公共方法进行切入。(被切入的 方法经过<tx:method>筛选)web应用程序最合适的事务切入点是Service的方法上。

----通过以上三个步骤设置好声明式事务后,当Service中 的业务方法被调用之前,Spring会获取事务对象并启动事务。并使用try-catch-finally来处理异常。业务方法执行成功则会提交事务,默认情况下如果抛出了RuntimeException 或者Rrror 对象就会回滚事务。(注意: 这里注意一下,在tx:method中配置了rollback_for 中配置的Exception 这个是运行时的异常才会回滚不然其他异常是不会回滚的!)

2.使用annotation配置

*1.在事务管理的dao实现类之前标注@Transactional

*2.在要进行事务管理的方法前加上@Transactional(propagation= Propagation.REQUIRED)

*3.在配置文件中指定驱动:<tx:annotation-driven transaction-manager="transactionManager" />

package demo.spring.dao;import java.util.Iterator;import java.util.List;import javax.sql.DataSource;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.transaction.annotation.Propagation;import org.springframework.transaction.annotation.Transactional;import demo.spring.entity.Person;@Transactional//将此类进行事务管理public class PersonDaoImpl implements PersonDao {private JdbcTemplate jt;public void setDataSource(DataSource dataSource){jt = new JdbcTemplate(dataSource);}@Overridepublic void insert(long id, String name, int age) {jt.update("insert into person values('"+id+"','"+name+"','"+age+"')");}@Transactional(propagation= Propagation.REQUIRED)//定义要事务管理的方法,指定传播行为public void batchInsert(List persons) {for(Iterator it = persons.iterator(); it.hasNext(); ){Person p = (Person) it.next();insert(p.getId(),p.getName(),p.getAge());}}}

0 0
原创粉丝点击