`
干宝周
  • 浏览: 14319 次
社区版块
存档分类
最新评论

Spring 事务简介

阅读更多

Introduction

事务是什么?

事务的作用

  1. 事务的特性
    1. Atomic原子性、Consistency一致性、Isolation隔离性和Durability持久性。
    2. 原子性:指整个事务是不可以分割的工作单元。只有事务中所有的操作执行成功,才算整个事务成功,事务中任何一个SQL语句执行失败,那么已经执行成功的SQL语句也必须撤销,数据库状态应该回到执行事务前的状态。
    3. 一致性:指数据库事务不能破坏关系数据的完整性以及业务逻辑上的一致性。例如对于银行转账事务,不管事务成功还是失败,应该保证事务结束后两个转账账户的存款总额是与转账前一致的。
    4. 隔离性:指的是在并发环境中,当不同的事务同时操纵相同的数据时,每个事务都有各自的完整数据空间。
    5. 持久性:指的是只要事务成功结束它对数据库所做的更新就必须永久保存下来。即使发生系统崩溃,重新启动数据库系统后,数据库还能恢复到事务成功结束时的状态。
      BEGIN;
      
      COMMIT;
      
  2. 使用场景
    简单举个例子就是你要同时修改数据库中两个不同表的时候,如果它们不是一个事务的话,当第一个表修改完,可是第二表改修出现了异常而没能修改的情况下,就只有第二个表回到未修改之前的状态,而第一个表已经被修改完毕。

jdbc处理事务的方法

  1. example
      try{
    
      Connection conn = getConnection(); // 不管如何我们得到了链接
    
      conn.setAutoCommit(false);
    
      // 插入订单
    
      // 修改库存
    
      conn.commit(); // 成功的情况下,提交更新。
    
      } catch(SQLException ex) {
    
      conn.rollback(); // 失败的情况下,回滚所有的操作
    
      } finally {
    
      conn.close();
    
      }
    
  1. 优点
    简单、直观
  2. 缺点
    1. 代码罗嗦
    2. 多个连接时,处理起来更罗嗦

现有系统中,对事务的处理

  1. 取出 ExecutorType.BATCH的session
  2. 一次传入多个sqlId,
    public static int[] batchUpdate(String sqlId, List<?> objList) {
    		int[] idArr = new int[objList.size()];
    
    		SqlSession session = null;
    		try {
    			session = sqlSessionFactory.openSession(ExecutorType.BATCH);
    			int index = 0;
    			for (Object obj : objList) {
    				idArr[index++] = session.update(sqlId, obj);
    			}
    			session.commit();
    		} catch (Exception e) {
    			logger.warn("batch update error:" + sqlId, e);
    			e.printStackTrace();
    		} finally {
    			if (session != null) {
    				session.close();
    			}
    		}
    		return idArr;
    	}
    
    
  1. 缺点
    1. 直接操作sqlId,service 和 dao不好分层
    2. 不支持update 和 select、insert交叉的情况
    3. 不能简单的支持跨db操作

spring里对事务的支持

spring为多种不同类型的事务管理机制提供统一编程模型,简化了对事务的使用。

spring处理事务的结构

Spring配置文件中关于事务配置总是由三个组成部分,分别是DataSource、TransactionManager和代理机制这三部分,无论哪种配置方式,一般变化的只是代理机制这部分。

  1. DataSource
    数据库连接源
  2. TransactionManager
    Spring的事务管理通过org.springframework.transaction.PlatformactionManager接口完成.PlatformTransactionManager是一个服务提供商接口(SPI),针对不同类型的底层事务框架,Spring提供了不同的PlatFormTransactionManager实现版本
    接口定义如下
    public interface PlatformTransactionManager {
     
      TransactionStatus getTransaction(TransactionDefinition definition)
        throws TransactionException;
     
      void commit(TransactionStatus status) throws TransactionException;
     
      void rollback(TransactionStatus status) throws TransactionException;
    }
    
  3. 代理机制
    用代理类来替代你真实操作db的方法

spring配置事务的方法

  1. http://blog.csdn.net/roroyangivan/article/details/7083385

Spring里对事务的使用

这里只介绍使用@Transactional注解的方法

简单的来说,只需要在DAO或者Service的类或者方法加上@Transactional的注解。 譬如

@Transactional
public class TransactionShowCaseService extends BaseService {

    public int insertWithoutException(User record) {
        return userMapper.insert(record);
    }
}

查看代码 testTransactionAdd() 和 testTransactionAddThrowException()
testTransactionMethod() 和 testNoTransactionMethod()

查看代码testNotSupportTransAction()

实现方法

AOP,在注解的方法完成之后,调用commit;
查看 代码 testTransactionWithSleep(),等事务完成之后,最后才提交.数据库记录为

[searcher b2c_product [unknown] 192.168.10.65 2014-03-29 15:13:31.009 CST]LOG:  execute S_1: BEGIN
[searcher b2c_product [unknown] 192.168.10.65 2014-03-29 15:13:31.011 CST]LOG:  execute <unnamed>: INSERT INTO testUser (id,userName,phone)
                        VALUES ($1,$2,$3)
[searcher b2c_product [unknown] 192.168.10.65 2014-03-29 15:13:31.011 CST]DETAIL:  parameters: $1 = '3033', $2 = NULL, $3 = '2123'

[searcher b2c_product [unknown] 192.168.10.65 2014-03-29 15:13:41.022 CST]LOG:  execute S_2: COMMIT

系统中曾出现的问题:
在事务里,先更新订单状态,然后调用外部接口。由于外部接口看不到这个事务里的修改,发现订单状态错误,于是报错

spring事务行为

传播行为定义了调用方法之间的事务边界。传播规则回答了这样的问题:新的时候应该被启动,还是挂起? 方法是否要在事务环境中运行

PROPAGATION_REQUIRED

当前事务必须出现在事务中,如果有则使用原来的session。否则,创建一个新的.
– 默认的行为

PROPAGATION_NOT_SUPPORT

不应该运行在事务中,如果当前存在事务,那么在该方法运行期间,事务被挂起。
查看代码 testNotSupportTransAction()

猜测 testNotSupport 的运行结果

DEBUG java.sql.Connection 2014-04-03 12:19:54 - ooo Connection Opened
DEBUG java.sql.PreparedStatement 2014-04-03 12:19:54 - ==>  Executing: INSERT INTO testUser (id,userName,phone) VALUES (?,?,?) 
DEBUG java.sql.PreparedStatement 2014-04-03 12:19:54 - ==> Parameters: 4154(Integer), null, 2123(String)
DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager 2014-04-03 12:19:54 - Suspending current transaction
DEBUG org.springframework.jdbc.datasource.DataSourceUtils 2014-04-03 12:19:54 - Fetching JDBC Connection from DataSource
DEBUG org.springframework.jdbc.datasource.DataSourceUtils 2014-04-03 12:19:54 - Registering transaction synchronization for JDBC Connection
DEBUG java.sql.Connection 2014-04-03 12:19:54 - ooo Connection Opened
DEBUG java.sql.PreparedStatement 2014-04-03 12:19:54 - ==>  Executing: SELECT id, userName, phone FROM testUser WHERE id = ? 
DEBUG java.sql.PreparedStatement 2014-04-03 12:19:54 - ==> Parameters: 4154(Integer)
DEBUG org.springframework.jdbc.datasource.DataSourceUtils 2014-04-03 12:19:54 - Returning JDBC Connection to DataSource
DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager 2014-04-03 12:19:54 - Resuming suspended transaction after completion of inner transaction
DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager 2014-04-03 12:19:54 - Initiating transaction commit

Propagation.MANDATORY

如果上下文不在事务中,则抛出异常。
运行代码 testErrorMandatory() , testOkMandatory()

Propagation_NEVER

不应该出现在事务上下文中,如果出现在上下文中,则抛出异常
testNever

Propagation_requires_new

必须运行在自己的事务中。如果已经存在事务,则当前事务会被挂起,自己另起一个事务

Propagation_supports

不需要上下文,但是如果存在当前的事务,那么这个方法会在这个事务中进行

Propagation.NEST

如果存在一个事务,则单独起一个嵌套的事务,对事物单独的提交。如果没有起事务,就启动一个新的事务. 貌似比较少见。。。。。看不太明白. 具体请参考 http://ahujyy.iteye.com/blog/1544304 
运行代码 testNest()

DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager 2014-04-03 11:44:16 - Switching JDBC Connection [DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager 2014-04-03 11:44:16 - Participating in existing transaction
DEBUG java.sql.Connection 2014-04-03 11:44:16 - ooo Connection Opened
DEBUG java.sql.PreparedStatement 2014-04-03 11:44:16 - ==>  Executing: INSERT INTO testUser (id,userName,phone) VALUES (?,?,?) 
DEBUG java.sql.PreparedStatement 2014-04-03 11:44:16 - ==> Parameters: 6238(Integer), null, 2123(String)
DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager 2014-DEBUG java.sql.PreparedStatement 2014-04-03 11:44:16 - ==>  Executing: INSERT INTO testUser (id,userName,phone) VALUES (?,?,?) 
DEBUG java.sql.PreparedStatement 2014-04-03 11:44:16 - ==> Parameters: 6238(Integer), null, 2123(String)
DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager 2014-04-03 11:44:16 - Releasing transaction savepoint
DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager 2014-04-03 11:44:16 - Initiating transaction commit
DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager 2014-0DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager 2014-0DEBUG org.springframework.jdbc.datasource.DataSourceUtils 2014-04-03 11:44:16 - Returning JDBC Connection to DataSource

回滚的策略

异常分类

  1. checked异常
    表示无效,不是程序中可以预测的。比如无效的用户输入,文件不存在,网络或者数据库链接错误。这些都是外在的原因,都不是程序内部可以控制的。
    必须在代码中显式地处理。比如try-catch块处理,或者给所在的方法加上throws说明,将异常抛到调用栈的上一层。
    继承自java.lang.Exception(java.lang.RuntimeException除外)。熟悉的checked 异常包括
    Java.lang.ClassNotFoundException
    Java.lang.NoSuchMetodException
    java.io.IOException
  2. unchecked异常
    表示错误,程序的逻辑错误。是RuntimeException的子类,比如IllegalArgumentException, NullPointerException和IllegalStateException。
    不需要在代码中显式地捕获unchecked异常做处理。
    继承自java.lang.RuntimeException(而java.lang.RuntimeException继承自java.lang.Exception)。
  1. Error
    当程序发生不可控的错误时,通常做法是通知用户并中止程序的执行。与异常不同的是Error及其子类的对象不应被抛出。
    Error是throwable的子类,代表编译时间和系统错误,用于指示合理的应用程序不应该试图捕获的严重问题。
    Error由Java虚拟机生成并抛出,包括动态链接失败,虚拟机错误等。程序对其不做处理。

回滚策略

Spring的事务管理默认只对出现unchecked excpetion (java.lang.RuntimeException及其子类)进行回滚。
如果一个方法抛出Exception或者Checked异常,Spring事务管理默认不进行回滚
查看代码 testTransactionAddThrowError() 以及 testThrowException()

配置回滚策略

  1. rollbackFor
  2. noRollbackFor
    提问,如果是子类,是否会回滚? 查看 testNoRollback() 和 testRollback()

超时

在达到超时时间之后,事务自动回滚. — 这个貌似有问题。稍后补充
看代码testTimeout()。 这个代码不会回滚

 @Transactional(timeout = 3, propagation = Propagation.REQUIRED)
    public void insertTimeout(User record) {

        userMapper.insert(record);
        try {
            Thread.sleep(4000);
        } catch (Exception e) {
            e.printStackTrace();
        }

        userMapper.insert(record);

    }

只读

数据库可能可以针对这个只读的事务做优化。
在只读业务里,更新操作会有异常.

事务的隔离性

多数据源+ 事务

代码 testInsertTwoDb(), 和 testInsertTwoUserReqireNew()

atomikos

http://iteye.blog.163.com/blog/static/1863080962012102945116222/

结论 && Future works

  1. 在spring里,事务比较强大。
  2. 还需要研究 事务的隔离性 、 timeout的具体用法
  3. 编程式事务也需要进一步研究
  4. 多数据源情况下,事务的使用

随意记录

  1. 如果想要使用@Transaction标注,必须在ApplicationContext里加上
    <!-- 事务管理 -->
    	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    		<property name="dataSource" ref="dataSource" />
    	</bean>
    
        <!-- 使用annotation定义事务 -->
        <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />
    
    
  1. 输出JDBC相关操作的日志配置
        <logger name="org.springframework" level="WARN" >
        </logger>
    
        <logger name="org.springframework.jdbc" level="DEBUG"/>
        <logger name="org.springframework.transaction" level="DEBUG"/>
    
    
    
        <logger name="java.sql" level="DEBUG" />
    
    
    

输出的日志文件类似于

DEBUG java.sql.Connection 2014-03-29 23:32:19 - ooo Connection Opened
DEBUG java.sql.PreparedStatement 2014-03-29 23:32:19 - ==>  Executing: INSERT INTO testUser (id,userName,phone) VALUES (?,?,?) 
DEBUG java.sql.PreparedStatement 2014-03-29 23:32:19 - ==> Parameters: 8983(Integer), null, 2123(String)
DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager 2014-03-29 23:32:29 - Initiating transaction commit
DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager 2014-0DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager 2014-0

附录

  1. http://blog.csdn.net/roroyangivan/article/details/7083385
  2. spring事务控制 http://www.cnblogs.com/ysxlin/archive/2008/06/06/1215300.html
  3. 异常的分类 http://woshixy.blog.51cto.com/5637578/1071966
  4. spring分布式事务 http://blog.chinaunix.net/uid-21162795-id-3424973.html 和http://iteye.blog.163.com/blog/static/1863080962012102945116222/
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics