本节讲述Spring中的事务管理。
Spring中的事务管理
事务就是一系列的动作,它们被当做一个单独的工作单元,这些动作要么全部完成,要么全部不起作用。用来确保数据的完整性和一致性。
事务的四个关键属性(ACID)
1.原子性(atomicity):事务是一个原子操作,由一系列动作组成。事务的原子性确保动作要么全部完成要么完全不起作用。
2.一致性(consistency):一旦所有事务动作完成,事务就被提交,数据和资源就处于一种满足业务规则的一致性状态中。
3.隔离性(isolation):可能有许多事务会同时处理相同的数据,因此每个事物都应该与其他事务隔离开来,防止数据破坏。
4.持久性(durability):一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,通常情况下,事务的结果被写到持久化存储器中。
Spring的事务管理机制
Spring在不同的事务管理API之上定义了一个抽象层。而应用程序开发人员不必了解底层的事务管理API,就可以使用Spring的事务管理机制。
Spring既支持编程式事务管理,也支持声明式的事务管理。
编程式事务管理:将事务管理代码嵌入到业务方法中来控制事务的提交和回滚。在编程式管理事务时,必须在每个事务操作中包含额外的事务管理代码。
声明式事务管理:大多数情况下比编程式事务管理更好用。它将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。事务管理作为一种横切关注点,可以通过AOP方法模块化。Spring通过SpringAOP框架支持声明式事务管理。
Spring中的事务管理器
Spring从不同的事务管理API中抽象了一整套的事务机制。开发人员不必了解底层的事务API,就可以利用这些事务机制。有了这些事务机制,事务管理代码就能独立于特定的事务技术了。
Spring的核心事务管理抽象是 org.springframework.transaction Interface Platform TransactionManager管理和封装了一组独立于技术的方法。无论使用Spring的哪种事务管理策略(编程式或声明式),事务管理器都是必须的。
Spring中事务管理器的不同实现
org.springframework.jdbc.datasource
Class DataSource TransactionManager:在应用程序中只需要处理一个数据源,而且通过JDBC存取。
org.springframework.transaction.jta
Class JtaTransactionManager:在JavaEE应用服务器上用JTA(Java Transaction API)进行事务管理。
org.springframework.orm.hibernate3
Class Hibernate TransactionManager:用Hibernate框架存取数据库。
事务管理器以普通的Bean形式声明在SpringIOC容器中。
声明式事务
一共三步。
1.在配置文件中配置事务管理器
2.在配置文件中配置注解驱动
3.在要应用事务的方法前添加@Transaction。
沿用上一节中的项目。在项目下新建包com.leezp.spring.tx。
在该包下新建接口BookShopDao.java。
|
|
新建异常类BookStockException.java。
|
|
新建异常类UserAccountException.java。
|
|
新建接口BookShopDao的实现类BookShopDaoImpl.java。
|
|
新建接口BookShopService.java。
|
|
新建接口BookShopService的实现类BookShopServiceImpl.java。注意在要应用事务的方法上添加@Transaction。
|
|
重新配置db.properties属性文件里的连接字符串。
|
|
修改applicationContext.xml配置文件。
|
|
向该配置文件中添加:
|
|
新建测试类(JUnit Test Case)SpringTransactionTest.java。
|
|
其他细节
事务的属性:
1.事务的传播行为 propagation
2.事务的隔离级别 isolation
3.对哪些异常不回滚 noRollbackFor/rollbackFor
4.事务的过期时间 timeout
事务传播属性
当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。
事务的传播行为可以由传播属性指定。Spring定义了7种传播行为。
Spring支持的事务传播行为
REQUIRED(默认):如果有事务在运行,当前的方法就在这个事务内运行,否则,就启动一个新的事务,并在自己的事务内运行。
REQUIRES_NEW:当前的方法必须启动新事务,并在它自己的事务内运行,如果有事务正在运行,应该将它挂起。
SUPPORTS:如果有事务在运行,当前的方法就在这个事务内运行,否则它可以不运行在事务中。
NOT_SUPPORTED:当前的方法不应该运行在事务中,如果有运行的事务,将它挂起。
MANDATORY:当前的方法必须运行在事务内部,如果没有正在运行的事务,就抛出异常。
NEVER:当前的方法不应该运行在事务中,如果有运行的事务,就抛出异常。
NESTED:如果有事务在运行,当前的方法就应该在这个事务的嵌套事务内运行,否则,就启动一个新的事务,并在它自己的事务内运行。
事务的传播属性可以在@Transactional注解的propagation属性中定义。eg:@Transactional(propagation=Propagation.REQUIRED)
在项目中新建接口Cashier.java。
|
|
新建接口Cashier的实现类CashierImpl.java。
|
|
修改SpringTransactionTest.java。
|
|
并发事务所导致的问题
当同一个应用程序或者不同应用程序中的多个事务在同一个数据集上并发执行时,可能会出现许多意外的问题。
并发事务所导致的问题可以分为下面三种类型:
1.脏读:对于两个事务T1,T2,T1读取了已经被T2更新但还没有被提交的字段。之后,若T2回滚,T1读取的内容就是临时且无效的。
2.不可重复读:对于两个事务T1,T2,T1读取了一个字段,然后T2更新了该字段。之后,T1再次读取同一个字段,值就不同了。
3.幻读:对于两个事务T1,T2,T1从一个表中读取了一个字段,然后T2在该表中插入了一些新的行。之后,如果T1再次读取同一个表,就会多出几行。
事务的隔离级别:
使用isolation指定事务的隔离级别,最常用的取值为 READ_COMMITTED:保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。
超时和只读属性
由于事务可以在行和表上获得锁,因此长事务会占用资源,并对整体性能产生影响。
如果一个事务只读取数据但不做修改,数据库引擎可以对这个事务进行优化。
超时事务属性:事务在强制回滚之前可以保持多久。这样可以防止长期运行的事务占用资源。
只读事务属性:表示这个事务只读取数据但不更新数据,这样可以帮助数据库引擎优化事务。
修改CashierImpl.java。
|
|
基于XML文件方式
新建包com.leezp.spring.tx.xml。复制com.leezp.spring.tx包下的BookShopDao.java、BookStockException.java、UserAccountException.java到com.leezp.spring.tx.xml包。
在该包下新建BookShopDaoImpl.java。
|
|
新建包com.leezp.spring.tx.xml.service。复制com.leezp.spring.tx.xml包下的Cashier.java、BookShopService.java到包com.leezp.spring.tx.xml.service。
新建包com.leezp.spring.tx.xml.service.impl。
在该包下新建BookShopServiceImpl.java。
|
|
在该包下新建CashierImpl.java。
|
|
新建配置文件applicationContext-tx-xml.xml。
|
|
在包com.leezp.spring.tx.xml下新建SpringTransactionTest.java。
|
|
版权声明:本文为博主原创文章,转载请注明出处 Leezp’s Blog