一、数据库事务概述
1、基本介绍
事务必需满足ACID(原子性、一致性、隔离性和持久性)特性,缺一不可:
- 原子性(Atomicity):即事务是不可分割的最小工作单元,事务内的操作要么全做,要么全不做;
- 一致性(Consistency):在事务执行前数据库的数据处于正确的状态,而事务执行完成后数据库的数据还是处于正确的状态,即数据完整性约束没有被破坏;如银行转帐,A转帐给B,必须保证A的钱一定转给B,一定不会出现A的钱转了但B没收到,否则数据库的数据就处于不一致(不正确)的状态。
- 隔离性(Isolation):并发事务执行之间无影响,在一个事务内部的操作对其他事务是不产生影响,这需要事务隔离级别来指定隔离性;
- 持久性(Durability):事务一旦执行成功,它对数据库的数据的改变必须是永久的,不会因比如遇到系统故障或断电造成数据不一致或丢失。
在实际项目开发中数据库操作一般都是并发执行的,即有多个事务并发执行,并发执行就可能遇到问题,目前常见的问题如下:
- 丢失更新:两个事务同时更新一行数据,最后一个事务的更新会覆盖掉第一个事务的更新,从而导致第一个事务更新的数据丢失,这是由于没有加锁造成的;
- 脏读:一个事务看到了另一个事务未提交的更新数据;
- 不可重复读:在同一事务中,多次读取同一数据却返回不同的结果;也就是有其他事务更改了这些数据;
- 幻读:一个事务在执行过程中读取到了另一个事务已提交的插入数据;即在第一个事务开始时读取到一批数据,但此后另一个事务又插入了新数据并提交,此时第一个事务又读取这批数据但发现多了一条,即好像发生幻觉一样。
为了解决这些并发问题,需要通过数据库隔离级别来解决,在标准SQL规范中定义了四种隔离级别:
- 未提交读(Read Uncommitted):最低隔离级别,一个事务能读取到别的事务未提交的更新数据,很不安全,可能出现丢失更新、脏读、不可重复读、幻读;
- 提交读(Read Committed):一个事务能读取到别的事务提交的更新数据,不能看到未提交的更新数据,不可能可能出现丢失更新、脏读,但可能出现不可重复读、幻读;
- 可重复读(Repeatable Read):保证同一事务中先后执行的多次查询将返回同一结果,不受其他事务影响,可能可能出现丢失更新、脏读、不可重复读,但可能出现幻读;
- 序列化(Serializable):最高隔离级别,不允许事务并发执行,而必须串行化执行,最安全,不可能出现更新、脏读、不可重复读、幻读。
2、事务类型
数据库事务类型有本地事务和分布式事务:
- 本地事务:就是普通事务,能保证单台数据库上的操作的ACID,被限定在一台数据库上;
- 分布式事务:涉及两个或多个数据库源的事务,即跨越多台同类或异类数据库的事务(由每台数据库的本地事务组成的),分布式事务旨在保证这些本地事务的所有操作的ACID,使事务可以跨越多台数据库;
Java事务类型有JDBC事务和JTA事务:
- JDBC事务:就是数据库事务类型中的本地事务,通过Connection对象的控制来管理事务;
- JTA事务:JTA指Java事务API(Java Transaction API),是Java EE数据库事务规范, JTA只提供了事务管理接口,由应用程序服务器厂商(如WebSphere Application Server)提供实现,JTA事务比JDBC更强大,支持分布式事务。
二、事务管理器
1、Spring事务管理的核心是事务管理器抽象,由PlatformTransactionManager接口定义:
public interface PlatformTransactionManager { // 根据TransactionDefinition, 返回一个已经激活的事务或创建一个新的事务 TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException; // 提交事务 void commit(TransactionStatus status) throws TransactionException; // 回滚事务 void rollback(TransactionStatus status) throws TransactionException; }
PlatformTransactionManager的两个内置实现:
- DataSourceTransactionManager:位于org.springframework.jdbc.datasource包中,数据源事务管理器,提供对单个javax.sql.DataSource事务管理,用于Spring JDBC抽象框架、MyBatis框架的事务管理;
- JtaTransactionManager:位于org.springframework.transaction.jta包中,提供对分布式事务管理的支持,并将事务管理委托给Java EE应用服务器事务管理器;
2、简单实例(编程式)
定义事务管理器
<bean id="mysql" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> ... </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="mysql"/> </bean>
测试
public class TransactionTest { private static ApplicationContext context; private static PlatformTransactionManager txManager; private static DataSource dataSource; private static JdbcTemplate jdbcTemplate; @BeforeClass public static void init() { context = new ClassPathXmlApplicationContext("spring-context.xml"); txManager = context.getBean(PlatformTransactionManager.class); dataSource = context.getBean(DataSource.class); jdbcTemplate = new JdbcTemplate(dataSource); } @Test public void testPlatformTransactionManager() { DefaultTransactionDefinition def = new DefaultTransactionDefinition(); def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED); def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); TransactionStatus status = txManager.getTransaction(def); jdbcTemplate.update("INSERT INTO `user` VALUES(170, 'hello3', 96)"); //txManager.commit(status); txManager.rollback(status); } }
3、事务属性介绍
事务属性通过TransactionDefinition接口实现定义,主要有事务隔离级别、事务传播行为、事务超时时间、事务是否只读
i)隔离级别
用于解决并发事务时出现的问题,由TransactionDefinition中的静态变量指定:
ISOLATION_DEFAULT:默认隔离级别,即使用底层数据库默认的隔离级别 ISOLATION_READ_UNCOMMITTED:未提交读 ISOLATION_READ_COMMITTED:提交读,一般情况下我们使用这个 ISOLATION_REPEATABLE_READ:可重复读 ISOLATION_SERIALIZABLE:序列化
ii)事务传播行为
事务传播行为用于指定在多个事务方法间调用时,事务是如何在这些方法间传播的,Spring共支持7种传播行为,由TransactionDefinition中的静态变量指定:
PROPAGATION_REQUIRED:如果当前存在一个逻辑事务,则加入该逻辑事务,否则将新建一个逻辑事务 PROPAGATION_REQUIRES_NEW:每次都创建新的逻辑事务 PROPAGATION_SUPPORTS:如果当前存在逻辑事务,就加入到该逻辑事务,否则就以非事务方式执行 PROPAGATION_NOT_SUPPORTED:如果当前存在逻辑事务,就把当前事务暂停,以非事务方式执行 PROPAGATION_MANDATORY:如果当前存在逻辑事务,使用当前事务执行,否则抛出异常 PROPAGATION_NEVER:不支持事务,如果当前存在事务则抛出异常 PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行,如果当前不存在事务,则创建一个新的事务,嵌套事务使用数据库中的保存点来实现,即嵌套事务回滚不影响外部事务,但外部事务回滚将导致嵌套事务回滚
iii)事务超时时间
设置事务的超时时间,单位为秒,默认为-1表示使用底层事务的超时时间;事务超时后会抛出异常,并导致自动回滚
new DefaultTransactionDefinition().setTimeout(..); // 设置单个事务
DataSourceTransactionManager. setDefaultTimeout(..); // 设置全局
iv)只读
new DefaultTransactionDefinition().setReadOnly(true);
三、