一,了解事务处理
1.1 基本概念
事务处理在运用程序中起着至关重要的作用,事务处理由若干个步骤组成,这些步骤间有着各种各样的逻辑关系,
一个事务中的所有步骤必须同时成功或失败。当所有步骤操作成功时,事务算完成,而当某个事务操作失败,
所有的步骤都需要回滚。对于提交和回滚的概念说明:
提交(commit):当所有的步骤被完整执行完成时,称该事务被提交。
回滚(rollback):当某个步骤操作异常时,所有的步骤都不能被提交,必须进行回滚,回滚到执行前的状态。
1.2事务处理的特征
原子性(Atomic):
事务必须是原子工作单元;对于其数据修改,要么全都执行,要么全都不执行。
通常,与某个事务关联的操作具有共同的目标,并且是相互依赖的。
如果系统只执行这些操作的一个子集,则可能会破坏事务的总体目标。原子性消除了系统处理操作子集的可能性。
一致性(Consistency):
事务在完成时,必须使所有的数据都保持一致状态。在相关数据库中,所有规则都必须应用于事务的修改,以保持所有数据的完整性。
事务结束时,所有的内部数据结构(如 B 树索引或双向链表)都必须是正确的。
某些维护一致性的责任由应用程序开发人员承担,他们必须确保应用程序已强制所有已知的完整性约束。
例如,当开发用于转帐的应用程序时,应避免在转帐过程中任意移动小数点。
隔离性(Isolation):
由并发事务所作的修改必须与任何其它并发事务所作的修改隔离。
事务查看数据时数据所处的状态,要么是另一并发事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看中间状态的数据。
这称为隔离性,因为它能够重新装载起始数据,并且重播一系列事务,以使数据结束时的状态与原始事务执行的状态相同。
当事务可序列化时将获得最高的隔离级别。在此级别上,从一组可并行执行的事务获得的结果与通过连续运行每个事务所获得的结果相同。
由于高度隔离会限制可并行执行的事务数,所以一些应用程序降低隔离级别以换取更大的吞吐量。
持久性(Duration):
事务完成之后,它对于系统的影响是永久性的。该修改即使出现致命的系统故障也将一直保持。
二,事务处理的三种形式
在了解spring的事务前,需要了解实际开发程序中事务处理的3中方式,这3种方式分别为:
关系型数据库事务处理,传统JDBC事务处理和分布式事务处理。
2.1 关系型数据库事务处理
关系型数据库提供了事务处理的能力,一般分为3个步骤告诉数据库:首先告诉数据库开启一个事务,
通知数据库把接下来的操作当作一个单元来执行,最后通知数据库是进行提交或回滚。
2.2传统JDBC事务处理
传统JDBC事务处理基于数据库也提供了事务处理的能力,用过JDBC的都知道,获取数据资源,开启事务,执行,提交或回滚,代码很繁琐。
2.3分布式事务处理
前面两种都是针对一个数据库处理,而最后这种会针对多个事务处理,东西很多。
三,spring的事务处理
3.1 关于spring事务处理的概述
上面说了一堆基本的简介,下面真正进入主题,spring提供了两种事务处理方式:
分别为编程式事务处理(programmatic transaction management)和声明式事务处理(declarative transaction management)。
spring中事务处理实际上是基于AOP的动态实现。为了实现动态AOP,spring在默认情况下会采用java动态代理的机制,需要实现
接口,在接口实现类中创建动态代理执行的方法以及方法的细节。
====事务核心接口类PlatformTransactionManager
在spring中,事务处理有其核心接口org.springframework.transaction.PlatformTransactionManager,该接口源码如下,
源码中英文注释太多,单独提方法:
public interface PlatformTransactionManager {
//目前的事务
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
//事务提交
void commit(TransactionStatus status) throws TransactionException;
//事务回滚
void rollback(TransactionStatus status) throws TransactionException;
}
对于该接口有很多不同的实现类,但是一般不是直接实现PlatformTransactionManager接口,
而是继承AbstractPlatformTransactionManager类,该类是PlatformTransactionManager实现类,是一个抽象类,
其他PlatformTransactionManager接口的实现类通过继承AbstractPlatformTransactionManager(抽象类)
来实现PlatformTransactionManager接口中的方法,同时还能使用AbstractPlatformTransactionManager中的其他方法。
====事务属性接口类 TransactionDefinition
在处理spring事务时,会用到一些常量的定义,spring事务的属性都定义在TransactionDefinition接口中,
该接口定义7中类型的事务和四种事务隔离级别等,TransactionDefinition接口源码提炼如下:
public interface TransactionDefinition {
//如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
int PROPAGATION_REQUIRED = 0;
//支持当前事务,如果当前没有事务,就以非事务方式执行。
int PROPAGATION_SUPPORTS = 1;
//使用当前的事务,如果当前没有事务,就抛出异常。
int PROPAGATION_MANDATORY = 2;
//新建事务,如果当前存在事务,把当前事务挂起。
int PROPAGATION_REQUIRES_NEW = 3;
//以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
int PROPAGATION_NOT_SUPPORTED = 4;
//以非事务方式执行,如果当前存在事务,则抛出异常。
int PROPAGATION_NEVER = 5;
//如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类 似的操作。
int PROPAGATION_NESTED = 6;
int ISOLATION_DEFAULT = -1;
//读未提交数据(Read Uncommitted)
int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED;
//读已提交数据(Read Committed)
int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED;
//可重复读(Repeatable Read)
int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ;
//串行化(Serializable)
int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE;
int TIMEOUT_DEFAULT = -1;
int getPropagationBehavior();
int getIsolationLevel();
int getTimeout();
boolean isReadOnly();
String getName();
}
=====事务状态接口类TransactionStatus
代表了当前事务的状态,该接口类继承与SavepointManager(保存点管理类)。源码如下:
public interface TransactionStatus extends SavepointManager {
//判断是否是一个新事务
boolean isNewTransaction();
//判断是否有保存点
boolean hasSavepoint();
//设定成只读事务
void setRollbackOnly();
//判断是否为回滚
boolean isRollbackOnly();
//判断事务是否完成
boolean isCompleted();
}
以上PlatformTransactionManager(核心),TransactionDefinition(属性),TransactionStatus(状态)为事务3个核心接口,
进行无限制拓展形成spring处理各种事务的设计和实现,还有很多辅助类。
3.2 编程式事务处理(programmatic transaction management)
spring提供TransactionTemplate能够以编程的方式实现事务处理。TransactionTemplate是无状态且线程安全的。
源码如下,摘了其中一些:
public class TransactionTemplate extends DefaultTransactionDefinition
implements TransactionOperations, InitializingBean {
/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());
//需要注入PlatformTransactionManager
private PlatformTransactionManager transactionManager;
//无参构造器
public TransactionTemplate() {
}
//构造器注入
public TransactionTemplate(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
public TransactionTemplate(PlatformTransactionManager transactionManager, TransactionDefinition transactionDefinition) {
super(transactionDefinition);
this.transactionManager = transactionManager;
}
//set注入
public void setTransactionManager(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
//事务执行完后调用
public void afterPropertiesSet() {
if (this.transactionManager == null) {
throw new IllegalArgumentException("Property ‘transactionManager‘ is required");
}
}
/**
* Return the transaction management strategy to be used.
*/
public PlatformTransactionManager getTransactionManager() {
return this.transactionManager;
}
//事务执行
public Object execute(TransactionCallback action) throws TransactionException {
if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);
}
else {
TransactionStatus status = this.transactionManager.getTransaction(this);
Object result = null;
try {
result = action.doInTransaction(status);
}
catch (RuntimeException ex) {
// Transactional code threw application exception -> rollback
rollbackOnException(status, ex);
throw ex;
}
catch (Error err) {
// Transactional code threw error -> rollback
rollbackOnException(status, err);
throw err;
}
this.transactionManager.commit(status);
return result;
}
}
//异常回滚
private void rollbackOnException(TransactionStatus status, Throwable ex) throws TransactionException {
logger.debug("Initiating transaction rollback on application exception", ex);
try {
this.transactionManager.rollback(status);
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by rollback exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException ex2) {
logger.error("Application exception overridden by rollback exception", ex);
throw ex2;
}
catch (Error err) {
logger.error("Application exception overridden by rollback error", ex);
throw err;
}
}
}
编程式事务处理实例:
jar包准备:准备好项目结构中的jar包,包括对应数据库驱动jar,spring.jar等。
在数据库中创建一个简单的表:
CREATE TABLE t_user_main(
ID NUMBER(11) PRIMARY KEY,
userName VARCHAR2(20),
age NUMBER(3)
);
(1)我的项目结构
(2)jar包准备(我的为oracle驱动jar包,根据数据库选择对应的驱动)
(3)需要进行事务管理的HelloDAO类
package com.lanhuigu.spring.dao; import javax.sql.DataSource; import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.DefaultTransactionDefinition; import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; public class HelloDAO { private DataSource dataSource; private PlatformTransactionManager transactionManager; //通过set方式注入 public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; } public void setTransactionManager(PlatformTransactionManager transactionManager) { this.transactionManager = transactionManager; } //=============编程式事务处理的3种编写形式==================== //第一种编写 public int create(String msg) { System.out.println(msg+"开始!"); //使用TransactionTemplate进行事务处理编程 TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager); //调用TransactionTemplate的execute方法 Object result = transactionTemplate.execute( new TransactionCallback() { //覆盖TransactionCallback的doInTransaction()方法 @Override public Object doInTransaction(TransactionStatus status) { // TODO Auto-generated method stub //在该方法内对数据库进行增加操作 System.out.println("添加对象进行"); JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); //第一次执行不打开,第二次执行打开 //jdbcTemplate.update("insert into t_user_main values(2,'test',24)"); jdbcTemplate.update("insert into t_user_main values(1,'test',24)"); return null; } } ); return 0; } //第二种编写 /*public int create(String msg) { System.out.println(msg+"开始!"); DefaultTransactionDefinition dtd = new DefaultTransactionDefinition(); TransactionStatus status = transactionManager.getTransaction(dtd); try { //使用JdbcTemplate与数据库进行交互 System.out.println("添加对象进行"); JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); jdbcTemplate.update("insert into t_user_main values(1,'test',24)"); } catch (DataAccessException ex) { // TODO: handle exception transactionManager.rollback(status); throw ex; } finally { transactionManager.commit(status); } return 0; }*/ //第三种编写 /*public int create(String msg) { System.out.println(msg+"开始!"); TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager); transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus status) { // TODO Auto-generated method stub JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); jdbcTemplate.update("insert into t_user_main values(1,'test',24)"); } }); return 0; }*/ }
(4)spring编程式事务处理配置
<?xml version="1.0" encoding="UTF-8"?> <!-- - Application context definition for JPetStore's business layer. - Contains bean references to the transaction manager and to the DAOs in - dataAccessContext-local/jta.xml (see web.xml's "contextConfigLocation"). --> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> <!-- spring编程式事务配置 --> <!-- 1.配置数据源(dataSource) --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <!-- 数据库驱动 --> <property name="driverClassName"> <value>oracle.jdbc.driver.OracleDriver</value> </property> <!-- 数据库连接 --> <property name="url"> <value>jdbc:oracle:thin:@localhost:1521/XE</value> </property> <!-- 数据库用户名 --> <property name="username"> <value>system</value> </property> <!-- 数据库密码 --> <property name="password"> <value>123456</value> </property> </bean> <!-- 2.设定transactionManager --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 这里配置的dataSource是DataSourceTransactionManager类中的属性, 在类中有着属性的定义:private DataSource dataSource; 跟下面配置的HelloDAO中的dataSource不是用在一个地方,是一个东西用在两个地方--> <property name="dataSource"> <ref bean="dataSource"/> </property> </bean> <!-- 3.配置事务管理目标类 --> <bean id="helloDAO" class="com.lanhuigu.spring.dao.HelloDAO"> <!-- dataSource属性 --> <property name="dataSource"> <ref bean="dataSource"/> </property> <!-- transactionManager属性 --> <property name="transactionManager"> <ref bean="transactionManager"/> </property> </bean> </beans>
(5)测试程序
package com.lanhuigu.spring.test; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.lanhuigu.spring.dao.HelloDAO; import com.lanhuigu.spring.impl.IUser; public class TestJDBCTemplate { @Test public void testJDBCTemplate(){ //1.创建spring容器 ApplicationContext actx = new ClassPathXmlApplicationContext("/applicationContext.xml"); //2.编程式测试 HelloDAO helloDAO = (HelloDAO) actx.getBean("helloDAO"); helloDAO.create("添加对象"); //3.声明式测试 /*HelloDAO helloDAO = (HelloDAO) actx.getBean("helloDAOProxy");//获取代理的bean helloDAO.create("添加对象");*/ } }
(6)编程式总结
运行上面测试程序,数据库里面通过jdbcTemplate.update("insert into t_user_main values(1,‘test‘,24)");插入了一条数据。
这只是使用了,是不是没体会到事务是如何控制的?把程序中注释掉的//jdbcTemplate.update("insert into t_user_main values(2,‘test‘,24)");
这行代码打开,再次运行测试程序,报错了,因为数据库主键的唯一性,再次使用jdbcTemplate.update("insert into t_user_main values(1,‘test‘,24)");
时,抛异常,但是,正常情况下jdbcTemplate.update("insert into t_user_main values(2,‘test‘,24)");是能够插入这条数据的,可是数据库确没有id为2的
这条数据,这是因为插入id为1,2的这两条数据在同一个事务中,如果这个事务中某一步执行失败,则整个事务回滚到执行前,所以id为2的数据没有插入
数据库,被回滚了,这就是简单的事务使用体验,一起成功,一起失败,不搞个人特殊。
3.2 声明式事务处理(declarative transaction management)
把编程式中的spring配置和HelloDAO以及测试程序修改如下:
HelloDAO:
package com.lanhuigu.spring.dao; import javax.sql.DataSource; import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.DefaultTransactionDefinition; import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; public class HelloDAO { private DataSource dataSource; private PlatformTransactionManager transactionManager; //通过set方式注入 public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; } public void setTransactionManager(PlatformTransactionManager transactionManager) { this.transactionManager = transactionManager; } //============声明式事务编程============ public int create(String msg) { System.out.println(msg+"开始"); JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); //新增数据 jdbcTemplate.update("insert into t_user_main values(1,'test',24)"); System.out.println(msg+"结束"); return 0; } }
spring声明式配置:
<?xml version="1.0" encoding="UTF-8"?> <!-- - Application context definition for JPetStore's business layer. - Contains bean references to the transaction manager and to the DAOs in - dataAccessContext-local/jta.xml (see web.xml's "contextConfigLocation"). --> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> <!-- spring编程式事务配置 --> <!-- 1.配置数据源(dataSource) --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <!-- 数据库驱动 --> <property name="driverClassName"> <value>oracle.jdbc.driver.OracleDriver</value> </property> <!-- 数据库连接 --> <property name="url"> <value>jdbc:oracle:thin:@localhost:1521/XE</value> </property> <!-- 数据库用户名 --> <property name="username"> <value>system</value> </property> <!-- 数据库密码 --> <property name="password"> <value>123456</value> </property> </bean> <!-- 2.设定transactionManager --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 这里配置的dataSource是DataSourceTransactionManager类中的属性, 在类中有着属性的定义:private DataSource dataSource; 跟下面配置的HelloDAO中的dataSource不是用在一个地方,是一个东西用在两个地方--> <property name="dataSource"> <ref bean="dataSource"/> </property> </bean> <!-- 3.配置事务管理目标类 --> <bean id="helloDAO" class="com.lanhuigu.spring.dao.HelloDAO"> <!-- dataSource属性 --> <property name="dataSource"> <ref bean="dataSource"/> </property> <!-- transactionManager属性 --> <!-- <property name="transactionManager"> <ref bean="transactionManager"/> </property> --> </bean> <!-- 声明式事务处理 --> <bean id="helloDAOProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="transactionManager"> <ref bean="transactionManager"/> </property> <property name="target"> <ref bean="helloDAO"/> </property> <property name="transactionAttributes"> <props> <prop key="create"> PROPAGATION_REQUIRED </prop> </props> </property> </bean> </beans>
测试程序:
package com.lanhuigu.spring.test; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.lanhuigu.spring.dao.HelloDAO; import com.lanhuigu.spring.impl.IUser; public class TestJDBCTemplate { @Test public void testJDBCTemplate(){ //1.创建spring容器 ApplicationContext actx = new ClassPathXmlApplicationContext("/applicationContext.xml"); //2.编程式测试 /*HelloDAO helloDAO = (HelloDAO) actx.getBean("helloDAO"); helloDAO.create("添加对象");*/ //3.声明式测试 HelloDAO helloDAO = (HelloDAO) actx.getBean("helloDAOProxy");//获取代理的bean helloDAO.create("添加对象"); } }
总结:
事务体验逻辑与编程式测试逻辑一样。
四,关于spring事务使用总结
小型软件用编程式,大型软件用声明式,一般推荐用声明式,声明式灵活而强大。