事务的基本原理Spring事务的本质其实就是数据库对事务的支持,使用JDBC的事务管理机制,就是利用java.sql.Connection对象完成对事务的提交,那在没有Spring帮我们管理事务之前,我们要怎么做。 Connection conn = DriverManager.getConnection();
try {
conn.setAutoCommit(false); //将自动提交设置为false
执行CRUD操作
conn.commit(); //当两个操作成功后手动提交
} catch (Exception e) {
conn.rollback(); //一旦其中一个操作出错都将回滚,所有操作都不成功
e.printStackTrace();
} finally {
conn.colse();
}
事务是一系列的动作,一旦其中有一个动作出现错误,必须全部回滚,系统将事务中对数据库的所有已完成的操作全部撤消,滚回到事务开始的状态,避免出现由于数据不一致而导致的接下来一系列的错误。事务的出现是为了确保数据的完整性和一致性,在目前企业级应用开发中,事务管理是必不可少的。 与事务相关的理论知识众所周知,事务有四大特性(ACID) 1.原子性(Atomicity)事务是一个原子操作,由一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用。 2.一致性(Consistency)事务在完成时,必须是所有的数据都保持一致状态。 3.隔离性(Isolation)并发事务执行之间无影响,在一个事务内部的操作对其他事务是不产生影响,这需要事务隔离级别来指定隔离性。 4.持久性(Durability)一旦事务完成,数据库的改变必须是持久化的。 在企业级应用中,多用户访问数据库是常见的场景,这就是所谓的事务的并发。事务并发所可能存在的问题: 我们可以在java.sql.Connection中看到JDBC定义了五种事务隔离级别来解决这些并发导致的问题: /**
* A constant indicating that transactions are not supported.
*/
int TRANSACTION_NONE = 0;
/**
* A constant indicating that
* dirty reads, non-repeatable reads and phantom reads can occur.
* This level allows a row changed by one transaction to be read
* by another transaction before any changes in that row have been
* committed (a 'dirty read'). If any of the changes are rolled back,
* the second transaction will have retrieved an invalid row.
*/
int TRANSACTION_READ_UNCOMMITTED = 1;
/**
* A constant indicating that
* dirty reads are prevented; non-repeatable reads and phantom
* reads can occur. This level only prohibits a transaction
* from reading a row with uncommitted changes in it.
*/
int TRANSACTION_READ_COMMITTED = 2;
/**
* A constant indicating that
* dirty reads and non-repeatable reads are prevented; phantom
* reads can occur. This level prohibits a transaction from
* reading a row with uncommitted changes in it, and it also
* prohibits the situation where one transaction reads a row,
* a second transaction alters the row, and the first transaction
* rereads the row, getting different values the second time
* (a 'non-repeatable read').
*/
int TRANSACTION_REPEATABLE_READ = 4;
/**
* A constant indicating that
* dirty reads, non-repeatable reads and phantom reads are prevented.
* This level includes the prohibitions in
* <code>TRANSACTION_REPEATABLE_READ</code> and further prohibits the
* situation where one transaction reads all rows that satisfy
* a <code>WHERE</code> condition, a second transaction inserts a row that
* satisfies that <code>WHERE</code> condition, and the first transaction
* rereads for the same condition, retrieving the additional
* 'phantom' row in the second read.
*/
int TRANSACTION_SERIALIZABLE = 8;
翻译过来这几个常量就是 隔离级别越高,意味着数据库事务并发执行性能越差,能处理的操作就越少。你可以通过conn.setTransactionLevel去设置你需要的隔离级别。 了解了基本的JDBC事务,那有了Spring,在事务管理上会有什么新的改变呢? Spring事务管理Spring事务管理的核心接口是PlatformTransactionManager int getIsolationLevel();// 返回事务的隔离级别
String getName();// 返回事务的名称
int getPropagationBehavior();// 返回事务的传播行为
int getTimeout(); // 返回事务必须在多少秒内完成
boolean isReadOnly(); // 事务是否只读,事务管理器能够根据这个返回值进行优化,确保事务是只读的
Spring事务的传播属性由上图可知,Spring定义了7个以PROPAGATION_开头的常量表示它的传播属性。
Spring事务的隔离级别
调用PlatformTransactionManager接口的getTransaction()的方法得到的是TransactionStatus接口的一个实现 void flush();//如果适用的话,这个方法用于刷新底层会话中的修改到数据库,例如,所有受影响的Hibernate/JPA会话。
boolean hasSavepoint(); // 是否有恢复点
boolean isCompleted();// 是否已完成
boolean isNewTransaction(); // 是否是新的事务
boolean isRollbackOnly(); // 是否为只回滚
void setRollbackOnly(); // 设置为只回滚
可以看出返回的结果是一些事务的状态,可用来检索事务的状态信息。 配置事务管理器介绍完Spring事务的管理的流程大概是怎么走的。接下来可以动手试试Spring是如何配置事务管理器的 <!-- 配置事务管理器 -->
<bean id='transactionManager' class='org.springframework.jdbc.datasource.DataSourceTransactionManager'>
<property name='dataSource' ref='dataSource' />
</bean>
这配置不是唯一的,可以根据自己项目选择的数据访问框架灵活配置事务管理器 配置了事务管理器后,事务当然还是得我们自己去操作,Spring提供了两种事务管理的方式:编程式事务管理和声明式事务管理,让我们分别看看它们是怎么做的吧。 编程式事务管理编程式事务管理我们可以通过PlatformTransactionManager实现来进行事务管理,同样的Spring也为我们提供了模板类TransactionTemplate进行事务管理,下面主要介绍模板类,我们需要在配置文件中配置 <!--配置事务管理的模板-->
<bean id='transactionTemplate' class='org.springframework.transaction.support.TransactionTemplate'>
<property name='transactionManager' ref='transactionManager'></property>
<!--定义事务隔离级别,-1表示使用数据库默认级别-->
<property name='isolationLevelName' value='ISOLATION_DEFAULT'></property>
<property name='propagationBehaviorName' value='PROPAGATION_REQUIRED'></property>
</bean>
TransactionTemplate帮我们封装了许多代码,节省了我们的工作。下面我们写个单元测试来测测。 //方便测试直接写的sql
@Override
public void insert(String sql, boolean flag) throws Exception {
dao.insertSql(sql);
// 如果flag 为 true ,抛出异常
if (flag){
throw new Exception('has exception!!!');
}
}
//获取总金额
@Override
public Integer sum(){
return dao.sum();
}
dao对应的sum方法 <select id='sum' resultType='java.lang.Integer'>
SELECT SUM(money) FROM tbl_account;
</select>
下面看看测试代码 package com.gray;
import com.gray.service.BaseSevice;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import javax.annotation.Resource;
/**
* Created by gray on 2017/4/8.
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {'classpath:spring-test.xml'})
public class TransactionTest{
@Resource
private TransactionTemplate transactionTemplate;
@Autowired
private BaseSevice baseSevice;
@Test
public void transTest() {
System.out.println('before transaction');
Integer sum1 = baseSevice.sum();
System.out.println('before transaction sum: ' sum1);
System.out.println('transaction....');
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
try{
baseSevice.insert('INSERT INTO tbl_account VALUES (100);',false);
baseSevice.insert('INSERT INTO tbl_account VALUES (100);',false);
} catch (Exception e){
//对于抛出Exception类型的异常且需要回滚时,需要捕获异常并通过调用status对象的setRollbackOnly()方法告知事务管理器当前事务需要回滚
status.setRollbackOnly();
e.printStackTrace();
}
}
});
System.out.println('after transaction');
Integer sum2 = baseSevice.sum();
System.out.println('after transaction sum: ' sum2);
}
}
当baseSevice.insert的第二个参数为false时,我们假设插入数据没有出现任何问题,测试结果如图所示: 声明式事务管理声明式事务管理有两种常用的方式,一种是基于tx和aop命名空间的xml配置文件,一种是基于@Transactional注解,随着Spring和Java的版本越来越高,大家越趋向于使用注解的方式,下面我们两个都说。 <tx:advice id='advice' transaction-manager='transactionManager'>
<tx:attributes>
<tx:method name='insert' propagation='REQUIRED' read-only='false' rollback-for='Exception'/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id='pointCut' expression='execution (* com.gray.service.*.*(..))'/>
<aop:advisor advice-ref='advice' pointcut-ref='pointCut'/>
</aop:config>
测试代码 @Test
public void transTest() {
System.out.println('before transaction');
Integer sum1 = baseSevice.sum();
System.out.println('before transaction sum: ' sum1);
System.out.println('transaction....');
try{
baseSevice.insert('INSERT INTO tbl_account VALUES (100);',true);
} catch (Exception e){
e.printStackTrace();
}
System.out.println('after transaction');
Integer sum2 = baseSevice.sum();
System.out.println('after transaction sum: ' sum2);
}
事务正常执行结果截图 <!-- 声明式事务管理 配置事物的注解方式注入-->
<tx:annotation-driven transaction-manager='transactionManager'/>
然后在需要事务管理的地方加上@Transactional注解,如: @Transactional(rollbackFor=Exception.class)
public void insert(String sql, boolean flag) throws Exception {
dao.insertSql(sql);
// 如果flag 为 true ,抛出异常
if (flag){
throw new Exception('has exception!!!');
}
}
rollbackFor属性指定出现Exception异常的时候回滚,遇到检查性的异常需要回滚,默认情况下非检查性异常,包括error也会自动回滚。 |
|
来自: liang1234_ > 《spring》