一.数据库事务基础知识
1.何为数据库事务
事务必须同时满足四个特性(ACID):原子性、一致性、隔离性和持久性。
原子性:表示组成一个事务的所有数据操作是一个不可分割的原子单元,只有所有操作都执行成功,整个事务才会提交。
一致性:事务操作成功后,数据库所处的状态和它的业务规则是一致的,即数据不会被破坏。
隔离性:在并发数据操作时,不同的事务拥有各自的数据空间,他们的操作不会对对方产生干扰。
持久性:一旦事务提交成功后,事务中所有的数据操作都必须持久化到数据库中。
2.数据并发的问题
数据并发导致的问题有:脏读、不可重复读、幻象读、第一类丢失更新、第二类丢失更新。
脏读、不可重复读、幻读三个问题都是由于事务A对数据进行修改、增加,事务B总是在做读操作。而丢失更新问题则是由于两事务都在对数据进行修改。
3.数据库锁机制
数据库通过锁的机制解决并发访问问题。按锁定的对象的不同,可分为表锁定和行锁定;从并发事务锁定的关系上看,可以分为共享锁定和独占锁定。
1)表共享锁定:通过 lock table in share mode 语句显示获得
2)行共享锁定:通过 lock table in row share mode 语句显示获得,或者通过 select for update
3)表独占锁定:通过 lock table in exclusive mode 语句显示获得
4)行独占锁定:通过 lock table in row exclusive mode 语句显示获得,或者通过 insert、update、delete
5)表共享行独占锁定:通过 lock table in share row exclusive mode 语句显示获得
4.事务隔离级别
虽然数据库为用户提供了锁的DML操作方式,但直接使用锁管理师非常麻烦的,因此数据库为用户提供了自动锁机制,只要用户指定会话的事务隔离级别,数据库就会分析事务中的SQL语句,然后自动为事务操作的数据资源添加合适的锁。
ANNSI/ISO SQL 92标准定义了4个等级的事务隔离级别。read uncommitted < read committed< repeatable read < serializale
注意:并不是所有的数据库都支持事务,即使数据库支持事务,也并非支持所有的事务隔离级别。
二.Spring为不同的持久化框架提供了不同的事务管理器实现类。
持久层技术 事务管理器类实现类
Spring JDBC 和 iBatis DataSourceTransactionManager
JPA JpaTransactionManager
Hibernate HibernateTransactionManager
JTA JtaTransactionManager---------具有多个数据源的全局事务使用该事务管理器
JDO org.springframework.orm.jdo.IdoTransactionManager 不常用
三.事务管理方式--Spring既支持编程式事务管理,也支持声明式的事务管理
编程式事务管理:通过代码控制事务的提交和回滚。
声明式事务管理:将事务管理代码从业务方法中分离出来,通过配置来实现事务管理。核心基于AOP实现。
1.编程式的事务管理
jdbc代码:conn.setAutoCommite(false);
Hibernate代码:session.beginTransaction();//开启事务
2.使用xml配置声明式事务
实现步骤:
1.引入spring-sop相关的4个jar文件
2.引入aop名称空间(xml配置方式需要引入)
3.引入tx名称空间(事务方式必须引入)
例如,我们想通过spring的声明事务来让以下接口的delete()和update()方法拥有写事务的能力,而其他方法只拥有读事务的能力。
package com.testMyBatis.service;
public class BookService { private BookDao bookdao; public List<Book> getAllBooks() { return bookdao.getAllBooks(); } public Book getBookById(int id) { return bookdao.getBookById(id); }
public int delete(int id) { return bookdao.delete(id); }
public int update(Book entity) { return bookdao.update(entity); } }
Spring基本配置
<!--配置事务管理器(不同的持久类对应不同的事物管理器实现类) -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource"></property>
</bean>
1)使用原始的TransactionProxyFactoryBean
<!--1.需要实现事务增强的目标业务Bean--> <bean id="BookServiceTarget" class="com.testMyBatis.service.BookService"> <property name="bookdao" ref="bookdao"></property> </bean> <!--2.使用事务代理工厂类为目标业务Bean提供事务增强--> <bean id="BookService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="transactionManager" ref="transactionManager"></property> <property name="target" ref="BookServiceTarget"></property> <property name="transactionAttribubutes" > <props> <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop><!--以get为前缀的方法拥有只读事务属性--> <prop key="*">PROPAGATION_REQUIRED</prop><!--目标bean中所有方法拥有可写事务属性--> </props> </property> </bean>
2)基于tx/aop命名空间的配置
<!--配置事务增强(如何管理事务?)-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="get*" read-only="true"/>
<tx:method name="delete" rollback-for="Exception"/>
<tx:method name="update" />
</tx:attributes>
</tx:advice>
<!--AOP配置,拦截那些方法(切入点表示式)+应用上面的事务增强配置-->
<aop:config>
<aop:pointcut id="serviceMethod" expression="execution("com.testMyBatis.service.BookService.*(..)")"/>
<aop:advisor advice-ref="serviceMethod" advice-ref="txAdvice"/>
</aop:config>
注意:<tx:method>元素属性有:
name:必须属性,与事务属性关联的方法,可使用通配符*
propagation:非必需元素,事务传播行为,默认值是REQUIRED
isolation:非必需元素,事务隔离级别,默认值是DEFAULT
timeout:非必需元素,超时,默认值是-1
read-only:非必需元素,读写事务,默认值是false
rollback-for:非必需元素,回滚,默认值是所有运行期异常都回滚
no-rollback-for:非必需元素,默认值是所有检查型异常不回滚
3.使用注解配置声明式事务
1.在需要添加事务控制的地方,即业务类使用@Transactional注解
注意:@Transactional的8个属性:
propagation:非必需元素,事务传播行为,默认值是Propagation_REQUIRED
isolation:非必需元素,事务隔离级别,默认值是Isolation_DEFAULT
timeout:非必需元素,超时,int型,以秒为单位
readOnly:非必需元素,读写事务,默认值是false
rollbackFor:非必需元素,回滚,默认为{},类型为Class[],例如{SQLException.calss},多个异常之间用逗号分隔
noRollbackFor:非必需元素,默认为{}
rollbackForClassName:非必需元素,回滚,默认为{},类型为String[]
noRollbackForClassName:非必需元素,默认为{}
2.基于aop/tx命名空间的配置
<!--支持注解驱动的事务管理,指定事务管理器(@Transactional使事务注解生效) --> <tx:annotation-driven transaction-manager="transactionManager" />
注意:<tx:annotation-driven >具有三个属性
1)transaction-manager:如果用户事务管理器id为transactionManager,可简化为<tx:annotation-driven />
2)proxy-target-class:如果为true,Spring将通过创建子类来代理业务类,如果为false,则使用基于接口的代理
3)order:如果业务类除事务切面外,还需要织入其他的切面,通过该属性可以控制织入顺序
注意事项:1.Spring建议在具体业务上使用@Transactional注解,这样,不管<tx:annotation-driven >的proxy-target-class的属性配置是哪个,业务类都会启用事务机制。
2.方法上的注解会覆盖类定义处的注解
3.如果一个应用具有多个事务管理器,如果我们希望不同的地方使用不同的事务管理器,只需像下面配置,为事务管理器标志一个名字
<bean id="transactionManager1" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="datasource"></property>
<qualifier value="tm1"/> </bean>
然后,用上注解@Transactional(tm1)