Spring声明式事务

来源:互联网 发布:软件设计师 证书 编辑:程序博客网 时间:2024/06/18 01:56

事务

事务是一组逻辑上的操作,要么全部操作都成功,要么全部失败!
这里写图片描述

用四个词解释事务

ACID

  • 原子性(Atomic):原子性保证事务的所有操作要么都发生,要么都不发生。
  • 一致性(Consistent):一个事务结束了(不管成功与否),系统所处的状态和它的业务规则是一致的。
  • 隔离性(Isolated):一个事务执行的时候不会受到其他事务的干扰。
  • 持久性(Durable):事务一旦结束,数据就永久的保存到了数据库。

事务的隔离级别

      多个事务同时进行,经常会操作同一个数据,在这种并发的情况下容易导致以下问题:

  • 脏度—–脏度发生在一个事务读取了另一个事务改写但尚未提交的数据。如果另外一个事务回滚了,那么第一个事务读取的这个数据就是无效的。
  • 不可重复读—–不可重复读发生在一个事务执行相同的查询两次货两次以上,但是每一次读取到的数据都不同。这通常是在两次查询之间有另外一个事务进行了更新。
  • 幻读—–幻读与不可重复读有些类似。当事务(T1)读取了N行数据后,事务(T2)插入了几条数据,幻读就发生了。在后来的查询中T1就会发现一些原来没有的数据。

      在理想状态下,事务之间将完全隔离,从而防止发生以上问题,可是完全隔离会严重影响性能。因为隔离经常牵涉到锁定数据库中的记录(而有时是锁定完整的数据表)。侵占性的锁定会阻碍并发,要求事务互相等待来完成工作。

      考虑到完全隔离会影响性能,而且并不是所有的程序都需要完全隔离,所以有时可以在事务隔离方面灵活处理。因此,就会有几个隔离级别:

  • 未提交读(READ_UNCOMMIT):允许读取尚未提交的数据。将会导致脏读、不可重复读和幻读。
  • 已提交读(READ_COMMIT):允许读取已经提交的数据。可以防止脏读,不可以防止不可重复读和幻读。
  • 可重复读(REPEATABLE_READ):对相同字段的读取是一致的,除非数据被当前事务本身改变。可以防止脏读、不可重复读。但是幻读仍有可能发生。
  • 串读(SERIZLIZABLE):完全服从ACID的隔离级别,确保不会发生上述三种情况,当然,这也是最慢的。

Spring中的事务管理

1、平台事务管理器

PlatformTransactionManager:平台事务管理器。
Spring不直接管理事务。但是它为不同的持久层框架提供了不同的PlatfromTransactionManager接口实现:

事务管理器 说明 org.springframework.jdbc.datasource.DataSourceTransactionManager 使用Spring JDBC或iBatis 进行持久化数据时使用 org.springframework.orm.hibernate3.HibernateTransactionManager 使用Hibernate3.0版本进行持久化数据时使用 org.springframework.orm.jpa.JpaTransactionManager 使用JPA进行持久化时使用 org.springframework.jdo.JdoTransactionManager 当持久化机制是Jdo时使用 org.springframework.transaction.jta.JtaTransactionManager 使用一个JTA实现来管理事务,在一个事务跨越多个资源时必须使用
2、事务定义

事务隔离级别:
这里写图片描述

事务传播行为:
这里写图片描述

3、事务状态

是否有保存点;
是否是一个新的事务;
事务是否已经提交。

三者的关系:
PlatformTransactionManager通过TransactionDefinition设置事务相关信息管理事务,管理事务过程中产生一些事务状态,由TransactionStatus记录。


事务操作环境准备

数据表的创建:

CREATE TABLE 'account' (  'id' INT(11) NOT NULL AUTO_INCREMENT,  'name' VARCHAR(20) NOT NULL,  'money' DOUBLE DEFAULT NULL,  PRIMARY KEY ('id')) ENGINE=INNODB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;INSERT INTO account VALUES ('1', 'aaa', '1000');INSERT INTO account VALUES ('2', 'bbb', '1000');INSERT INTO account VALUES ('3', 'ccc', '1000');

Dao的创建:

public interface AcountDao {    public void from(String fromName,double money);    public void to(String toName,double money);}

Dao的实现类:

public class AcountDaoImpl extends JdbcDaoSupport  implements AcountDao {    @Override    public void from(String fromName, double money) {        String sql = "UPDATE account SET money = money - ? WHERE NAME=?";        this.getJdbcTemplate().update(sql,money,fromName);    }    @Override    public void to(String toName, double money) {        String sql = "UPDATE account SET money = money + ? WHERE NAME=?";        this.getJdbcTemplate().update(sql,money,toName);    }}

Service接口:

public interface AcountService {    public void transfer(String FromName,String ToName,double money);}

Service的实现:

public class AcountServiceImpl implements AcountService {    private AcountDao acountDao;    public void setAcountDao(AcountDao acountDao) {        this.acountDao = acountDao;    }    @Override    public void transfer(String FromName, String ToName, double money) {        acountDao.from(FromName, money);        acountDao.to(ToName, money);    }}

applicationContext.xml的配置:

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xmlns:context="http://www.springframework.org/schema/context"    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.xsd    http://www.springframework.org/schema/context    http://www.springframework.org/schema/context/spring-context.xsd    http://www.springframework.org/schema/aop    http://www.springframework.org/schema/aop/spring-aop.xsd    http://www.springframework.org/schema/tx     http://www.springframework.org/schema/tx/spring-tx.xsd">    <context:property-placeholder location="classpath:jdbc.properties"/>    <!-- 配置C3P0连接池 -->    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">        <property name="driverClass" value="${jdbc.driver}"/>        <property name="jdbcUrl" value="${jdbc.url}"/>        <property name="user" value="${jdbc.user}"/>        <property name="password" value="${jdbc.password}"/>    </bean>    <bean id="acountDao" class="Demo01.AcountDaoImpl">        <property name="dataSource" ref="dataSource"/>    </bean>    <bean id="acountService" class="Demo01.AcountServiceImpl">        <property name="acountDao" ref="acountDao"/>    </bean></beans>

测试类:

@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration("classpath:applicationContext.xml")public class Demo01Test {    @Autowired    @Qualifier("acountService")    private AcountService acountService;    @Test    public void test() {        acountService.transfer("aaa", "bbb", 100d);    }}

完成了此次事务管理实验环境的搭建!



编程式事务管理

注册事务管理器:

<!-- 注册事务管理器 --><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">        <property name="dataSource" ref="dataSource"/></bean>

注册事务模板类:

<!-- 注册事务模板类 --><bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">        <property name="transactionManager" ref="transactionManager"/></bean>

向Service层注入模板类:
ServiceImpl添加:

private TransactionTemplate transactionTemplate;public void setTransactionTemplate(TransactionTemplate transactionTemplate) {        this.transactionTemplate = transactionTemplate;}

配置文件添加:

<bean id="acountService" class="Demo01.AcountServiceImpl">        <property name="acountDao" ref="acountDao"/>        <property name="transactionTemplate" ref="transactionTemplate"/></bean>

在ServiceImpl中使用模板实现事务的管理:

public void transfer(final String FromName, final String ToName, final double money) {transactionTemplate.execute(new TransactionCallback() {        @Override        public Object doInTransaction(TransactionStatus status) {            acountDao.from(FromName, money);            int a = 1/0;//在转账过程中产生异常            acountDao.to(ToName, money);            return null;        }    }); }

我在转账过程中产生异常,如果不进行事务管理,会导致aaa扣款,bbb未增款。经过此次编程式事务管理,经测试成功!


声明式事务管理(最原始)

基于TransactionProxyFactoryBean.
导入AOP相应jar包。

第一步:注册事务管理器

第二步:创建代理对象:

<!-- 创建代理对象 -->    <bean id="acountServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">        <!-- 对哪个对象生成代理 -->        <property name="target" ref="acountService"/>        <!-- 注入事务管理器 -->        <property name="transactionManager" ref="transactionManager"/>        <!-- 事务的属性设置 -->        <property name="transactionAttributes">            <props>                <prop key="transfer">PROPAGATION_REQUIRED</prop><!--设置事务的传播行为 -->            </props>        </property>    </bean> 

prop标签的格式:

PROPAGATION ISOLATION readOnly -Exception +Exception 传播行为 隔离级别 只读      遇到此异常回滚 遇到此异常提交



可以发现,这种方式有一个很大的缺点,我们需要为每一个管理事务的类生成代理。并且每一个都要配置。


声明式事务管理(自动代理)重点

导入AspectJ的jar包
第一步:注册事务管理器

第二步:定义增强:

    <!-- 定义增强 -->    <tx:advice id="txAdvice" transaction-manager="transactionManager">        <!-- 事务属性的配置 -->        <tx:attributes>            <tx:method name="transfer" />        </tx:attributes>    </tx:advice>

这里写图片描述

第三步:定义AOP配置

<!-- aop配置,定义切点和切面的信息 -->    <aop:config>        <!-- 定义切点,哪些类的哪些方法应用增强 -->        <aop:pointcut expression="execution(* Demo03.AcountService+.*(..))" id="myPointCut"/>        <!-- 定义切面 -->        <aop:advisor advice-ref="txAdvice" pointcut-ref="myPointCut"/>    </aop:config>

第四步:测试类

@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration("classpath:applicationContext3.xml")public class Demo03Test {    @Autowired    @Qualifier("acountService")//不需要注入代理了,因为生成这个对象时候就是代理对象了    private AcountService acountService;    @Test    public void test() {        acountService.transfer("aaa", "bbb", 100d);    }}

声明式事务管理(基于注解)

第一步:注册事务管理器

第二步:注解事务

<!-- 开启注解的事务管理 -->    <tx:annotation-driven transaction-manager="transactionManager"/>

第三步:在Service上使用注解

@Transactional    @Override    public void transfer(String FromName, String ToName, double money) {        acountDao.from(FromName, money);        int a=1/0;        acountDao.to(ToName, money);    }

这里写图片描述



原创粉丝点击