`

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  
                      

相关推荐

Global site tag (gtag.js) - Google Analytics