`

Spring事务管理

阅读更多
事务的ACID特性:
Atomic 原子性;Consistency 一致性;Isolation 隔离性; Durability 持久性。
在常用的关系数据库中,依赖日志和锁机制来保证事务具有ACID特性

事务的隔离级别:
未提交读 read uncommitted
提交读 read committed
重复读 repeatable read
序列化读 serializable

隔离级别的效果:
脏读:
脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。

不可重复读:
是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。例如,一个编辑人员两次读取同一文档,但在两次读取之间,作者重写了该文档。当编辑人员第二次读取文档时,文档已更改。原始读取不可重复。如果只有在作者全部完成编写后编辑人员才可以读取文档,则可以避免该问题。

幻读:
是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。例如,一个编辑人员更改作者提交的文档,但当生产部门将其更改内容合并到该文档的主复本时,发现作者已将未编辑的新材料添加到该文档中。如果在编辑人员和生产部门完成对原始文档的处理之前,任何人都不能将新材料添加到文档中,则可以避免该问题。

不可重复读的重点是修改 :
同样的条件 ,   你读取过的数据 ,   再次读取出来发现值不一样了
幻读的重点在于新增或者删除
同样的条件 ,   第 1 次和第 2 次读出来的记录数不一样

基于元数据的 Spring 声明性事务 :
Isolation 属性一共支持五种事务设置,具体介绍如下:
DEFAULT 使用数据库设置的隔离级别 ( 默认 ) ,由 DBA 默认的设置来决定隔离级别 .
READ_UNCOMMITTED 会出现脏读、不可重复读、幻读 ( 隔离级别最低,并发性能高 )
READ_COMMITTED  会出现不可重复读、幻读问题(锁定正在读取的行)
REPEATABLE_READ 会出幻读(锁定所读取的所有行)
SERIALIZABLE 保证所有的情况不会发生(锁表)


Spring框架将所有事务管理都抽象为PlatformTransactionManager、TransactionStatus、TransactionDefinition 3个接口

PlatformTransactionManager 定义了事务管理器,所有与事务相关的操作都有其管理
TransactionStatus 定义了事务的状态
TransactionDefinition 定义了事务隔离级别和传播行为
在启动事务时,PlatformTransactionManager根据TransactionDefinition来启动合适的事务



编程式事务管理
public class Main {

  public static void main(String[] args) {
    HsqldbUtil.startDatabase();
    ApplicationContext context = new ClassPathXmlApplicationContext("config.xml");
    DataSource dataSource = (DataSource)context.getBean("dataSource");
    queryAllBooks(dataSource);
    // 获取PlatformTransactionManager:
    PlatformTransactionManager transManager = (PlatformTransactionManager)context.getBean("transactionManager");
    try {
      doTransaction(transManager, dataSource);
    }
    finally {
      queryAllBooks(dataSource);
      System.exit(0);
    }
  }

  private static void doTransaction(PlatformTransactionManager transManager, DataSource dataSource) {
    // 定义TransactionDefinition:
    DefaultTransactionDefinition transDef = new DefaultTransactionDefinition();
    transDef.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
    transDef.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
    // 开始一个Transaction:
    TransactionStatus ts = transManager.getTransaction(transDef);
    try {
      insertBook(dataSource);
      //insertBook(dataSource);
      // 如果插入两次,则主键冲突
    }
    catch(RuntimeException e) {
      e.printStackTrace();
      // 回滚事务:
      transManager.rollback(ts);
      throw e;
    }
    catch(Error e) {
      e.printStackTrace();
      // 回滚事务:
      transManager.rollback(ts);
      throw e;
    }
    // 提交事务:
    transManager.commit(ts);
  }

  private static void queryAllBooks(DataSource dataSource) {
    List<Book> books = new JdbcTemplate(dataSource).query("select * from Book", new BookRowMapper());
    System.err.println("-- All Books ---------------------------");
    for(Book book : books) {
      System.err.println("  " + book.getName() + ", by " + book.getAuthor());
    }
    System.err.println("----------------------------------------");
  }

  private static void insertBook(DataSource dataSource) {
    JdbcTemplate jdbcTemp = new JdbcTemplate(dataSource);
    jdbcTemp.update(
        "insert into Book(id, name, author) values(?, ?, ?)",
        new Object[]{"123-456-789", "New Book", "New Author"});
  }
}

class BookRowMapper implements RowMapper {

  public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
    Book book = new Book();
    book.setId(rs.getString("id"));
    book.setName(rs.getString("name"));
    book.setAuthor(rs.getString("author"));
    return book;
  }
}


// 另一种方式无需捕获异常的方式,
TransactionTemplate tt = new TransactionTemplate(transManager);
tt.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
tt.execute(new TransactionCallback() {
        public Object doInTransaction(TransactionStatus status) {
          return null;
        }
      });


  <!-- 定义DataSource -->

  <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="org.hsqldb.jdbcDriver" />
    <property name="url" value="jdbc:hsqldb:mem:bookstore" />
    <property name="username" value="sa" />
    <property name="password" value="" />
  </bean>

  <!-- 定义PlatformTransactionManager -->

  <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>





如果要显式的回滚事务,没必要抛出RunTimeException,可以调用TransactionStatus.setRollBackOnly()方法


声明式事务
我们先使用Spring提供的TransactionProxyFactoryBean来实现具有声明式事务功能的BookDao,
TransactionProxyFactoryBean是一个具有AOP代理功能的FactoryBean
  <!-- 定义TransactionManager -->
  <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
    <property name="dataSource" ref="dataSource"/>
  </bean>
  <!-- 定义BookDao -->
  <bean id="bookDaoTarget" class="example.chapter6.BookDaoImpl">
    <property name="dataSource" ref="dataSource" />
  </bean>
  <bean id="bookDao" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
    <property name="target" ref="bookDaoTarget" />
    <property name="transactionManager" ref="transactionManager" />
    <property name="transactionAttributes">
      <props>
        <!-- 对以query开头的方法要求只读事务 -->
        <prop key="query*">PROPAGATION_REQUIRED,readOnly</prop>
        <!-- 对于其他方法要求事务 -->
        <prop key="*">PROPAGATION_REQUIRED</prop>
      </props>
    </property>
  </bean>


默认情况下,Spring只在RuntimeException异常或Error抛出时回滚事务,如果抛出CheckedException异常,Spring仍会提交事务
如果需要回滚,则在TransactionProxyFactoryBean配置中显示添加
<prop key="*">PROPAGATION_REQUIRED,-RemoteException</prop>



使用<tx:>简化配置,此时客户端自动获得的是具有事务功能的aop代理对象,不能获得上例中的bookDaoTarget
  <!-- 声明TxAdvice -->
  <tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
      <!-- 对以query开头的方法要求只读事务 -->
      <tx:method name="query*" read-only="true" />
      <!-- 对于其他方法要求事务 -->
      <tx:method name="*" rollback-for="java.io.IOException" />
    </tx:attributes>
  </tx:advice>

  <aop:config>
    <!-- 使用AspectJ语法定义Pointcut,需要导入aspectjweaver.jar -->
    <aop:pointcut id="bookDaoOperation" expression="execution(* example.chapter6.BookDao.*(..))" />
    <!-- 织入 -->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="bookDaoOperation" />
  </aop:config>



基于Java5注解简化配置
配置文件中加入
  <!-- 将所有具有@Transactional注解的Bean自动配置为声明式事务支持 -->
  <tx:annotation-driven transaction-manager="transactionManager"/>


在方法上添加@Transactional注解,
如果加到接口上,只支持java动态代理,如果加到实现类上,还能支持CGlib





总结如下:

    Spring配置文件中关于事务配置总是由三个组成部分,分别是DataSource、TransactionManager和代理机制这三部分,无论哪种配置方式,一般变化的只是代理机制这部分。
    DataSource、TransactionManager这两部分只是会根据数据访问方式有所变化,比如使用Hibernate进行数据访问时,DataSource实际为SessionFactory,TransactionManager的实现为 HibernateTransactionManager。

根据代理机制的不同,总结了五种Spring事务的配置方式,配置文件如下:
第一种方式:每个Bean都有一个代理
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
  <property name="configLocation" value="classpath:hibernate.cfg.xml" />
  <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
</bean>

<!-- 定义事务管理器(声明式的事务) -->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
  <property name="sessionFactory" ref="sessionFactory" />
</bean>

<!-- 配置DAO -->
<bean id="userDaoTarget" class="com.bluesky.spring.dao.UserDaoImpl">
  <property name="sessionFactory" ref="sessionFactory" />
</bean>

<bean id="userDao" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
  <!-- 配置事务管理器 -->
  <property name="transactionManager" ref="transactionManager" />
  <property name="target" ref="userDaoTarget" />
  <property name="proxyInterfaces" value="com.bluesky.spring.dao.GeneratorDao" />
  <!-- 配置事务属性 -->
  <property name="transactionAttributes">
    <props>
      <prop key="*">PROPAGATION_REQUIRED</prop>
    </props>
  </property>
</bean>




第二种方式:所有Bean共享一个代理基类
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
  <property name="configLocation" value="classpath:hibernate.cfg.xml" />
  <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
</bean>

<!-- 定义事务管理器(声明式的事务) -->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
  <property name="sessionFactory" ref="sessionFactory" />
</bean>

<bean id="transactionBase" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
  lazy-init="true" abstract="true">
  <!-- 配置事务管理器 -->
  <property name="transactionManager" ref="transactionManager" />
  <!-- 配置事务属性 -->
  <property name="transactionAttributes">
    <props>
      <prop key="*">PROPAGATION_REQUIRED</prop>
    </props>
  </property>
</bean>

<!-- 配置DAO -->
<bean id="userDaoTarget" class="com.bluesky.spring.dao.UserDaoImpl">
  <property name="sessionFactory" ref="sessionFactory" />
</bean>

<bean id="userDao" parent="transactionBase">
  <property name="target" ref="userDaoTarget" />
</bean>




第三种方式:使用拦截器
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
  <property name="configLocation" value="classpath:hibernate.cfg.xml" />
  <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
</bean>

<!-- 定义事务管理器(声明式的事务) -->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
  <property name="sessionFactory" ref="sessionFactory" />
</bean>

<bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
  <property name="transactionManager" ref="transactionManager" />
  <!-- 配置事务属性 -->
  <property name="transactionAttributes">
    <props>
      <prop key="*">PROPAGATION_REQUIRED</prop>
    </props>
  </property>
</bean>

<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
  <property name="beanNames">
    <list>
      <value>*Dao</value>
    </list>
  </property>
  <property name="interceptorNames">
    <list>
      <value>transactionInterceptor</value>
    </list>
  </property>
</bean>

<!-- 配置DAO -->
<bean id="userDao" class="com.bluesky.spring.dao.UserDaoImpl">
  <property name="sessionFactory" ref="sessionFactory" />
</bean>




第四种方式:使用tx标签配置的拦截器
  <context:annotation-config />
  <context:component-scan base-package="com.bluesky" />

  <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <property name="configLocation" value="classpath:hibernate.cfg.xml" />
    <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
  </bean>

  <!-- 定义事务管理器(声明式的事务) -->
  <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
  </bean>

  <tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
      <tx:method name="add*" propagation="REQUIRED" />
      <tx:method name="delete*" propagation="REQUIRED" />
      <tx:method name="update*" propagation="REQUIRED" />
      <tx:method name="*" read-only="true" />
    </tx:attributes>
  </tx:advice>

  <aop:config>
    <aop:pointcut id="interceptorPointCuts" expression="execution(* com.bluesky.spring.dao.*.*(..))" />
    <aop:advisor advice-ref="txAdvice" pointcut-ref="interceptorPointCuts" />
  </aop:config>


第五种方式:全注解
<context:annotation-config />
<context:component-scan base-package="com.bluesky" />

<tx:annotation-driven transaction-manager="transactionManager" />

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
  <property name="configLocation" value="classpath:hibernate.cfg.xml" />
  <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
</bean>

<!-- 定义事务管理器(声明式的事务) -->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
  <property name="sessionFactory" ref="sessionFactory" />
</bean>

此时在DAO上需加上@Transactional注解,如下:

@Transactional
@Component("userDao")
public class UserDaoImpl extends HibernateDaoSupport implements UserDao {

    public List<User> listUsers() {
        return this.getSession().createQuery("from User").list();
    }
}
  • 大小: 53.6 KB
分享到:
评论
1 楼 sljackson 2014-06-12  
                      

相关推荐

    Spring事务管理Demo

    Spring事务管理的目的是确保数据的一致性和完整性,尤其是在多操作、多资源的环境中。本Demo将深入探讨Spring如何实现事务的管理。 首先,Spring提供了两种主要的事务管理方式:编程式事务管理和声明式事务管理。 ...

    Spring事务管理开发必备jar包

    本资源包提供了进行Spring事务管理开发所需的所有关键库,包括框架基础、核心组件、AOP(面向切面编程)支持、日志处理、编译工具以及与数据库交互的相关jar包。下面将对这些知识点进行详细解释: 1. **Spring框架*...

    spring事务管理

    ### Spring事务管理详解 #### 一、Spring事务管理概述 Spring框架提供了强大的事务管理功能,使得开发者能够更方便地管理应用程序中的事务。Spring事务管理主要包括两种类型:编程式事务管理和声明式事务管理。 -...

    spring 事务管理的理解

    Spring 框架是Java开发中...理解并熟练掌握Spring事务管理,对于提升应用程序的稳定性和可靠性至关重要。在实际开发中,结合声明式事务管理、事务传播行为、隔离级别和回滚规则,可以有效地确保数据的完整性和一致性。

    Spring事务管理.pdf

    Spring事务管理.pdf 1.资料 2.本地事务与分布式事务 3.编程式模型 4.宣告式模型

    Synchronized锁在Spring事务管理下线程不安全

    Synchronized锁在Spring事务管理下,导致线程不安全。

    Spring事务管理的jar包

    本篇将深入探讨Spring事务管理的核心概念、工作原理以及如何使用`spring-tx-3.2.0.RELEASE.jar`这个jar包。 首先,我们需要理解什么是事务。在数据库系统中,事务是一组操作,这些操作被视为一个整体,要么全部完成...

    Spring事务管理失效原因汇总

    标题“Spring事务管理失效原因汇总”指出了本文的核心内容是分析在使用Spring框架进行事务管理时可能遇到的问题及其原因。描述部分进一步说明了事务失效的后果往往不明显,容易在测试环节被忽略,但在生产环境中出现...

    spring事务管理5种方法

    本篇文章将深入探讨Spring事务管理的五种方法,旨在帮助开发者更好地理解和运用这一核心特性。 首先,我们来了解什么是事务。在数据库操作中,事务是一组逻辑操作,这些操作要么全部成功,要么全部失败,确保数据的...

    Spring事务管理4种方式

    本文将详细介绍Spring事务管理的四种方式:编程式事务管理、声明式事务管理、PlatformTransactionManager接口以及TransactionTemplate。 1. **编程式事务管理**:这是一种手动控制事务的方式,通过在代码中调用`...

    详细介绍Spring事务管理

    ### Spring事务管理详解 #### 一、Spring事务管理的重要性及必要性 在现代软件开发中,事务管理是一项至关重要的技术,特别是在涉及数据库操作时。事务能够确保一系列操作要么全部成功,要么全部失败,这对于保持...

    spring事务管理.rar

    Spring事务管理是Spring框架的核心特性之一,它提供了一种强大且灵活的方式来管理应用程序中的事务边界。在企业级Java应用中,事务处理是确保数据一致性、完整性和可靠性的关键部分。本篇文章将深入探讨Spring的事务...

    spring事务管理.doc

    总的来说,Spring事务管理提供了一种灵活、强大的方式来处理应用程序中的事务,无论是在简单还是复杂的事务场景下,都能有效保证数据的一致性和完整性。通过声明式事务管理,开发者可以将关注点从事务细节中解脱出来...

    Spring事务管理配置文件问题排查

    当出现像描述中那样的问题——SQL语句执行出错但事务未回滚时,我们需要深入理解Spring事务管理的配置和机制。以下是一些关键知识点: 1. **Spring事务管理类型**: - **编程式事务管理**:通过`...

    spring事务管理几种方式代码实例

    spring事务管理几种方式代码实例:涉及编程式事务,声明式事务之拦截器代理方式、AOP切面通知方式、AspectJ注解方式,通过不同方式实例代码展现,总结spring事务管理的一般规律,从宏观上加深理解spring事务管理特性...

    Spring事务管理和SpringJDBC思维导图

    在思维导图"Spring Transaction.twd"中,可能包含了Spring事务管理的各个概念和它们之间的关系,如事务的ACID属性(原子性、一致性、隔离性和持久性),事务管理器,以及声明式和编程式事务管理的实现方式。...

    Spring事务管理的方法

    ### Spring事务管理的方法 #### 一、引言 在企业级应用开发中,事务管理是一项至关重要的技术。Spring框架作为Java领域中一个非常流行的轻量级框架,为开发者提供了多种方式来实现事务管理,其中主要分为编程式...

    spring事务管理1

    Spring事务管理确保每个事务都是独立的,防止了脏读、不可重复读和幻读等问题。脏读是指事务T1读取了事务T2未提交的修改;不可重复读是指事务T1在不同时间读取同一数据时得到不同结果,因为T2在这期间做了修改;幻读...

Global site tag (gtag.js) - Google Analytics