一、事务的相关概念
1.事务:在软件开发领域全部执行或者全部不执行的操作称之为事务。
2. 事务的ACID特征:
-
原子性:Atomicity,一次事务就是一个执行工作单元,一个工作单元中包含多个步骤,这些步骤要么全部执行完成,要么全部执行失败,不允许停留在中间的任一步骤,当事务执行失败后,会回滚到事务最开始的状态,就行没有执行过事务单元一样。不可分割性。
-
一致性:Consistency,事务封装了事件的状态,在多实例并发执行的情况下,必须保证各个实例之间状态的一致性。要么全部成功要么全部失败。
-
隔离性:Isolation,在多并发执行的情况下,每个事务单元之间不能互相影响,事务单元与事务单元之间保持隔离。
-
持久性:Durability,事务执行完成之后,事务所改变的事件状态必须持久安全地保存起来。保存到数据库中。
Spring中的事务就是对接不同数据库驱动框架的事务,进而执行数据库的事务管理,Spring事务的本质其实就是对数据库事务的支持。
3. 在企业级应用中,多用户访问数据库是很常见的场景,这就是所谓的事务的并发,事务并发所可能存在的问题:
-
脏读:一个事务读到另一个事务未提交的更新数据。
-
不可重复读:一个事务两次读同一行数据,可是两次读到的数据不一样。
-
幻读:一个事务执行两次查询,但第二次查询比第一次查询多出了一些数据行。
-
丢失数据:撤销一个事务时,把其他事务已提交的更新的数据覆盖了。
二、Spring事务的相关概念
Spring中事务相关的属性配置定义在TransactionDefinition接口中,可以查看Spring源码看到相关定义,可以使用不同的TransactionDefinition接口实现类来控制事务的相关属性。
1. 事务隔离级别:指若干个并发的事务之间的隔离程度,TransactionDefinition接口中定义了五个表示隔离级别的常量:
-
TransactionDefinition.ISOLATION_DEFAULT:这是默认值,表示使用底层数据库的默认隔离级别,对大部分数据库而言,通常这值就是TransactionDefinition.ISOLATION_READ_COMMITTED。
-
TransactionDefinition.ISOLATION_READ_UNCOMMITTED:该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据,该级别不能防止脏读和不可重复读,所以一般很少使用该隔离级别。
-
TransactionDefinition.ISOLATION_READ_COMMITTED:该隔离级别表示一个事务只能读取另一个事务已经提交的数据,该级别可以防止脏读,该级别也是大多数情况下的推荐值。
-
TransactionDefinition.ISOLATION_REPEATABLE_READ:该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同,即使在多次查询之间有新增的数据满足该查询,这些新增的记录也会被忽略,该级别可以防止脏读和不可重复读。
-
TransactionDefinition.ISOLATION_SERIALIZABLE:所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说该级别可也防止脏读、不可重复读以及幻读,但是这将严重影响程序的性能,通常情况下也不会用到该级别。
2. 事务传播行为:如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定当前事务执行的行为,在TransactionDefinition定义中包括了几个表示传播行为的常量:
-
TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务,这是Spring默认的传播行为,适用绝大多数情况。
-
TransactionDefinition.PROPAGATION_REQUIRES_NEW:如果当前存在事务,则把当前事务挂起,创建一个新的事务。
-
TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
-
TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则抛出异常。
-
TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
-
TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
3. 事务超时时间:指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务,在TransactionDefinition
中以int的值来表示时间,单位是秒。
4. 事务的只读属性:指对事务性资源进行只读操作或者是读写操作,所谓事务性资源就是指那些被事务管理的数据库连接,如果确定只对事务性资源进行只读操作,那么可以将事务标志为只读的,以提高事务处理的性能,在TransactionDefinition中以boolean类型来表示该事务是否只读。
5. Spring事务管理器:
Spring通过事务管理器来进行事务管理,Spring中定义了一个PlatformTransactionManager接口,该接口用来对接不同数据库驱动框架,也就是说不同数据库驱动框架以不同的方式实现了该接口。(Spring管理不同的数据库驱动框架,不同的数据库驱动框架管理数据库。)
a. JDBC及iBATIS、MyBatis框架事务管理器:
<!--构建spring对接jdbc的事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--只需要注入了一个数据源-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置一个JdbcTemplate,用来操作数据库-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
b. Hibernate事务管理器:
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<!--需要配置一个Hibernate sessionFactory Bean,这是Hibernate框架封装数据源的方式-->
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
6. Spring数据库连接配置
<!-- 引入配置文件,将配置文件中的变量引入Spring容器中,属于全局变量 -->
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:jdbc.properties" />
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/dormitory_manage_db" />
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
在Java中,我们通过DriverManager类来获取数据库连接;在Spring中,我们通过DriverManagerDataSource类来获取数据库连接。
三、Spring编程式事务管理
Spring事务管理分为两种类型的事务管理:编程式事务管理和声明式事务管理。
我们需要在代码中显示调用beginTransaction()、commit()、rollback()等事务管理相关的方法,这就是编程式事务管理。
通常使用DefaultTransactionDefinition类来进行事务属性设置,查看该类可以看出,它实现了TransactionDefinition接口。
public void aaddUser() {
//创建一个适合于当前业务的事务属性配置对象
DefaultTransactionDefinition transactionDefinition =
new DefaultTransactionDefinition();
//设置事务的传播行为
transactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
//设置事务的隔离级别
transactionDefinition.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
//设置事务的超时时间
transactionDefinition.setTimeout(10);
//设置只读
transactionDefinition.setReadOnly(true);
//获得事务状态
TransactionStatus status = transactionManager.getTransaction(transactionDefinition);
try {
jdbcTemplate.update("INSERT INTO \n" +
"user_info\n" +
"(id,name,username,password,sex)\n" +
"VALUES\n" +
"(4,'lisi','lisi','123123','男')");
} catch(Exception e) {
e.printStackTrace();
transactionManager.rollback(status);
}
transactionManager.commit(status);
}
四、Spring声明式事务管理
在Spring编程事务管理中,每写一个DAO操作方法,就需要显示地编写事务控制代码,这显然是冗余的代码段,而且这些代码段都是通用的事务功能,我们可以考虑使用AOP切面编程来进行相关代码的处理优化。
我们可以手动编写AOP切面编程的代码来进行相关的事务控制,但是Spring自身也提供了相关事务控制的切面编程方式,这种方式更加安全可靠。
基于< tx:advice/>标签进行声明式管理:
<!--引入事务管理器,并借助tx:advice来设置事务通知内容,声明一个Bean,该Bean进行事务管理-->
<tx:advice id="myAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="add*"
read-only="true"
propagation="REQUIRED"
isolation="READ_COMMITTED"
timeout="10"
rollback-for="java.lang.Exception"/>
<tx:method name="query*" read-only="true"/>
</tx:attributes>
</tx:advice>
<!--事务管理切面配置-->
<aop:config>
<!--定义切点-->
<aop:pointcut id="pointcut" expression="execution(* com.ychs.ssm.unit02.services.*.*(..))"/>
<!--定义通知,一个通知一个切点的类型使用这种标签,该标签是Spring内部标签-->
<aop:advisor advice-ref="myAdvice" pointcut-ref="pointcut"/>
</aop:config>
< tx:advice/>标签能够设置的事务:
-
事务传播行为:propagation
-
隔离级别:isolation
-
事务读/写:read-only
-
事务超时时间:timeout
-
事务回滚触发设置,任何RuntimeException将触发事务回滚