事务是将多个操作封装到一个事务中。作为事务。这些过程将被视为一个操作,从而保证所有操作要么都成功要么全部回滚,就像这些操作从来没发生过。
事务的四大特性[ACID]
1. 原子性:事务是由一个或者多个活动所组成的一个工作单元。原子性确保事务中的所有操作全部发生或者全部不发生。
2. 一致性:一旦事务完成(不管成功还是失败),系统必须确保它所建模的业务处于一致的状态。现实的数据不应该被破坏。
3. 隔离性:事务允许多个用户对相同的数据进行操作,每个用户的操作不会与其他用户纠缠在一起。因此,事务应该被彼此隔离,避免发生同步读写相同数据的事情。
4. 持久性:一旦完成事务,事务的结果应该持久化,这样就能从任何的系统崩溃中恢复过来。这一般会涉及将结果存储到数据库或者其他形式的持久化存储中。
事务管理器
Spring并不直接管理事务,而是提供多种事务管理器,它们将事务管理的职责委托给持久化机制所提供的平台相关的事务实现。为了使用事务管理器,你需要将其声明在应用程序上下文中。
JDBC事务管理器
如上图,如果使用的是JDBC来持久化,那么DataSourceTransactionManager会为你处理事务边界。
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
其实DataSourceTransactionManager是通过调用java.sql.Connection来管理事务,而后者是通过DataSource获取的。通过Connect的commit()方法来提交事务,事务失败则通过rollback()方法进行回滚。
Hibernate事务管理器
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
sessionFactory属性需要装配一个Hibernate SessionFactory,这里我们将其命名为sessionFoctory。HibernateTransactionManager将事务管理委托给org.hibernate.Transasction对象,后者从Hibernate Session中获取。当事务成功时HibernateTransaction Manager会调用Transaction对象的commit()方法。失败则是rollback()方法。
Java持久化API事务[JPA]
Hibernate多年来一直是事实上的Java持久化标准,但是现在Java持久化API作为真正的Java持久化标准进入大家的视野。
如果采用JPA你需要配置JpaTransactionManager
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"></property>
</bean>
JpaTransactionManager只需要装配一个JPA实体管理工厂。JpaTransactionManager将与由工厂所产生的JPA EntityManager合作来构建事务。
声明式事务
Spring对声明式事务管理的支持是通过使用Spring AOP框架实现的。这是很自然的一件事,因为事务是在应用程序功能之上的系统级服务。你可以将Spring事务想象成将方法包装上事务边界的切面。
定义事务的属性
Spring中声明式事务是通过事务属性来定义。事务属性描述了事务策略如何应用到方法。事务属性分别有:传播行为、隔离级别、回滚规则、事务超时、是否只读。
传播行为
传播行为定义了客户端与被调用方法之间的事务边界。传播规则回答了这样一个问题,即新的事务应该被启动还是被挂起,或者方法是否要在事务中运行。
Spring定义了7种不同的传播行为:
传播行为 | 含义 |
PROPAGATION_REQUIRED | 表示当时方法必须运行在事务中,如果当前事务存在,方法将会在该事务中运行。否则,会启动一个新的事务。 |
PROPAGATION_REQUIRED_NEW | 表示当前方法必须运行在自己的事务中,一个新的事务将被启动。如果存在当前事务,在该方法执行期间当前事务将被挂起。 |
PROPAGATION_SUPPORTS | 表示当前方法不需要事务上下文,但是如果存在当前事务的话,那么该方法会在这个事务中运行。 |
PROPAGATION_MANDATORY | 表示该方法必须运行在事务中,如果当前事务不存在,则会抛出异常。 |
PROPAGATION_NESTED | 表示如果当前已经存在一个事务,那么该方法将会嵌套事务中运行,嵌套的事务可以独立于当前事务进行单独地提交或者回滚。如果当前的事务不存在,那么其行为与PROPAGATION_REQUIRED一样。 |
PROPAGATION_NEVER | 表示当前方法不应该运行在事务上下文中。如果有当前事务在运行,则抛出异常 |
PROPAGATION_NOT_SUPPORTED | 表示该方法不应该运行在事务中,如果存在当前事务,在该方法运行期间,当前事务将被挂起。 |
传播行为解释
例如:传播行为是:PROPAGATION_REQUIRED
// PROPAGATION_REQUIRED
methodA(){
methodB();
}
// PROPAGATION_REQUIRED
methodB(){
// …
}
try{
con=getConnection();// 获取链接
con.setAutoCommit(false);// 手动提交事务
methodA();
con.commit();// 提交事务
}catch(Exception ex){
con.rollback();// 回滚事务
}finally{
close();// 释放资源
}
当调用A时会开启一个新事务,A和B会使用同一个连接,B会加入到当前事务中。隔离级别定义了一个事务可能受到其他并发事务事务的影响。
隔离级别
在应用中,多个事务并发运行,经常会操作相同的数据来完成各自的任务。这会导致以下问题:
1.读脏数据:这个发生在一个事务读取了另一个事务改写的但未提交的数据,如果数据被回滚,则读取的数据就是无效的数据
2.不可重复读:这个通常发生在一个事务执行多次查询,但是每次查询的结果都不一样,这是因为另一个并发的事务在两次查询期间更新了数据。
3.幻读:这通常发生在事务T1读取几行数据,并发事务又删除或者插入了一些数据,然后T1就会查询到一些不存在的数据
[幻读和不可重复读看似最后表现结果都是两次结果不一致,其实前者只需要锁住满足条件的记录即可,而后者需要锁住满足条件且其相近的记录]
在理想情况下,事务之间是完全隔离的,可以防止出现上述问题,但是事务隔离会导致性能问题,因为它会锁住数据库中的记录或者整张表。所以并不是所有的应用程序都需要隔离,Spring事务管理提供了下面几种隔离级别:
隔离级别 | 含义 |
ISOLATION_DEFAULT | 使用数据库默认的隔离级别 |
ISOLATION_READ_UNCOMMITTED | 允许读取尚未提交的数据变更,可能会读脏数据、幻读、不可重复读 |
ISOLATION_READ_COMMITTED | 允许读取并发事务已经提交的数据,可以阻止读脏数据,但是幻读和不可重复读仍可能发生 |
ISOLATION_REPEATABLE_READ | 对同一字段多次读取结果一致,除非数据是被本身事务改变,可以阻止读脏数据和不可重复读,但幻读仍可能发生 |
ISOLATION_SERIALIZABLE | 完全服从ACID的隔离级别,确保阻止读脏数据、不可重复读和幻读,最高的隔离级别。因为它是通过完全锁住事务相关的数据库来实现的 |
只读
如果事务只对后端的数据库进行只读操作,数据库可以利用事务的只读特性来进行一些优化。因为只读优化是在事务启动的时候由数据库实施,只有对那些具备启动一个新的事务传播行为的方法来说,将事务声明为只读才有意义。
在hibernate中,将事务声明为只读会导致Hibernate的flush模式为设置为FLUSH_NEVER。这会告诉Hibernate避免和数据库进行不必要的对象同步,并将所有更新延迟到事务结束。
事务超时
假设事务运行时间变得非常长,因为,事务可能涉及对后端的数据库的锁定,所以长时间的事务会不必要的占用数据库的资源。你可以声明一个事务,在特定的时间后自动回滚,而不是等待结束。
回滚规则
回滚规则定义了那些异常会回滚,那些不会。默认情况下,事务只有遇到了运行时异常才回滚,而遇到检查时异常不回滚。但是你可以声明事务在那些特定的异常时进行回滚,那些不回滚。
在xml中定义事务要使用xml配置事务必须要引入tx名称空间
先配置事务
<!-- 声明事务策略 | transaction-manager:关联事务管理器-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 定义事务属性 -->
<tx:attributes>
<!-- 为某些方法定义事务参数 -->
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="**" propagation="SUPPORTS" read-only="true"/>
</tx:attributes>
</tx:advice>
在<tx:method>中有多个属性帮助定义方法事务策略
隔离级别 | 含义 |
isolation | 指定事务的隔离级别 |
propagation | 定义事务的传播行为 |
read-only | 指定事务为只读 |
回滚规则 | rollback-for指定事务对于那些检查型异常应当回滚 no-rollback-for指定事务对于那些异常应当继续运行而不回滚 |
timeout | 设置事务执行的超时时间 |
最后添加切面,通知那些bean会被应用。
<!-- 配置切面来使用事务 -->
<aop:config>
<aop:pointcut expression="execution(* Transaction.Manager.BookServiceImpl.*(..))" id="pt1"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"/>
</aop:config>
总汇事务的xml配置:
<!-- 配置C3P0连接池,目的:管理数据库连接 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- 数据库连接配置 -->
<property name="driverClass" value="${mysqlDriver}"/>
<property name="jdbcUrl" value="${mysqlUrl}"/>
<property name="user" value="${mysqlUsername}"/>
<property name="password" value="${mysqlPassword}"/>
</bean>
<!-- 配置jdbc数据操作模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置JDBC事物管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 声明事务策略 | transaction-manager:关联事务管理器-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 定义事务属性 -->
<tx:attributes>
<!-- 为某些方法定义事务参数 -->
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="**" propagation="SUPPORTS" read-only="true" timeout="1"/>
</tx:attributes>
</tx:advice>
<!-- 配置切面来使用事务 -->
<aop:config>
<aop:pointcut expression="execution(* Transaction.Manager.BookServiceImpl.*(..))" id="pt1"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"/>
</aop:config>
<!— 其他配置 -->
注解方式配置事务
只需要在xml配置文件中加入以下一行xml代码就可以。
<!-- 启用事物注解扫描 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<tx:annotation-driven>会告诉Spring检查上下文中所有Bean并查找使用@Transactional注解的Bean,而不管这个注解是用在类级别还是方法级别上。对于每个使用@Transactional注解的Bean,<tx:annotation-driven>会自动为它添加事务通知。通知事务属性是通过@Transactional注解的参数来定义的。
@Transactional(
propagation=Propagation.REQUIRED,
isolation=Isolation.DEFAULT,
readOnly=true,
timeout=2
)
@Service("bookShippingService")
publicclass BookServiceImpl implements BookService {
@Autowired
private BookDao bookDao;
@Override
@Transactional(
propagation=Propagation.REQUIRES_NEW,
isolation=Isolation.SERIALIZABLE,
readOnly=false
)
publicint purchaseBook(User user) {
intres = bookDao.updateUser(user);
returnres;
}
}