Spring事务管理 && 事务传播行为

Spring----事务管理

回顾事务

事务的作用

  1. 当数据库操作序列中个别操作失败时,提供一种方式使数据库状态恢复到正常状态(A),保障数据库
    即使在异常状态下仍能保持数据一致性(C)(要么操作前状态,要么操作后状态)。
  2. 当出现并发访问数据库时,在多个访问间进行相互隔离,防止并发访问操作结果互相干扰(I)。
    事务特征(ACID)
    ◆ 原子性(Atomicity)指事务是一个不可分割的整体,其中的操作要么全执行或全不执行
    ◆ 一致性(Consistency)事务前后数据的完整性必须保持一致
    ◆ 隔离性(Isolation)事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的
    事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离
    ◆ 持久性(Durability)持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性
    的,接下来即使数据库发生故障也不应该对其有任何影响

事务隔离级

  • 脏读:允许读取未提交的信息

    ◆ 原因:Read uncommitted

    ◆ 解决方案: Read committed(表级读锁)

  • 不可重复读:读取过程中单个数据发生了变化

    ◆ 解决方案: Repeatable read (行级写锁)

  • 幻读:读取过程中数据条目发生了变化

    ◆ 解决方案: Serializable(表级写锁)

spring中的事务

设想一个场景,模拟银行的转账业务,用户A给用户B转款100,我们在业务层需要调用 加钱的数据操作 和 减钱的数据操作,这是两个会话,两个独立的事务。
案例代码:

web层:

ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
AccountService accountService = (AccountService) ctx.getBean("accountService");
accountService.transfer("Jock1","Jock2",100D);

service:

public void transfer(String outName, String inName, Double money) {
        accountDao.inMoney(outName,money); 
    	int i = 1 / 0;   //手动模拟出现异常
        accountDao.outMoney(inName,money);
}

dao:采用配置文件映射实体类方式

<update id="inMoney">
        update account set money = money + #{money} where name = #{name}
</update>

<update id="outMoney">
    update account set money = money - #{money} where name = #{name}
</update>

由此看以看出两个对数据库的操作是独立的。如果在两个事务之间还有其他代码,那么中间的代码出现异常,那么就会出现一个事务执行成功,一个事务执行失败的情况。(假设是事务自动提交前提下)

在spring中,dao的实现类是交给spring进行同意管理的,那么spring有没有对业务进行同意管理呢?将多个业务同意进行管理,让指定的多个业务之间拥有原子类的特性。下面的东西spring的解决方案。

Spring事务核心对象

J2EE开发使用分层设计的思想进行,对于简单的业务层转调数据层的单一操作,事务开启在业务层或者
数据层并无太大差别,当业务中包含多个数据层的调用时,需要在业务层开启事务,对数据层中多个操
作进行组合并归属于同一个事务进行处理。

  • Spring为业务层提供了整套的事务解决方案

    ◆ PlatformTransactionManager 平台事务管理器实现类

    ◆ TransactionDefinition 事务的基本信息

    ◆ TransactionStatus

PlatformTransactionManager

平台事务管理器实现类
◆ DataSourceTransactionManager 适用于Spring JDBC或MyBatis
◆ HibernateTransactionManager 适用于Hibernate3.0及以上版本
◆ JpaTransactionManager 适用于JPA
◆ JdoTransactionManager 适用于JDO
◆ JtaTransactionManager 适用于JTA

这是一个接口,定义了spring对事务管理的规范。对于不同数据库有不同的实现类。我们使用的是 DataSourceTransactionManager

这个接口的主要功能是:开启事务,提交事务。

此接口定义了事务的基本操作

获取事务 :

TransactionStatus getTransaction(TransactionDefinition definition)

提交事务 :

void commit(TransactionStatus status)

回滚事务 :

void rollback(TransactionStatus status)

TransactionDefinition

此接口定义了事务的基本信息

◆ 获取事务定义名称

String getName()

◆ 获取事务的读写属性

boolean isReadOnly()

◆ 获取事务隔离级别

int getIsolationLevel()
事务隔离级别取值
int ISOLATION_DEFAULT = -1;
int ISOLATION_READ_UNCOMMITTED = 1
int ISOLATION_READ_COMMITTED = 2
int ISOLATION_REPEATABLE_READ = 4
int ISOLATION_SERIALIZABLE = 8

◆ 获取超时时间

int getTimeout()

◆ 获取事务传播行为特征

int getPropagationBehavior()

TransactionStatus

此接口定义了事务在执行过程中某个时间点上的状态信息及对应的状态操作

◆ 获取事务是否处于新开启事务状态

boolean isNewTransaction()

◆ 获取事务是否处于已完成状态

boolean isCompleted()

◆ 获取事务是否处于回滚状态

boolean isRollbackOnly()

◆ 刷新事务状态

void flush()

◆ 获取事务是否具有回滚存储点

boolean hasSavepoint()

◆ 设置事务处于回滚状态

void setRollbackOnly()

管理事务-----编程式事务

使用java代码的方式对需要统一的事务进行管理

private AccountDao accountDao;
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
}
public void transfer(String outName,String inName,Double money){
    //创建事务管理器
    DataSourceTransactionManager dstm = new DataSourceTransactionManager();
    //为事务管理器设置与数据层相同的数据源
    dstm.setDataSource(dataSource);
    //创建事务定义对象
    TransactionDefinition td = new DefaultTransactionDefinition();
    //创建事务状态对象,用于控制事务执行
    TransactionStatus ts = dstm.getTransaction(td);
    accountDao.inMoney(outName,money);
    int i = 1/0; //模拟业务层事务过程中出现错误
    accountDao.outMoney(inName,money);
    //提交事务
    dstm.commit(ts);
}

这个方法中原本有两个独立的事务,但是现在,多了一个平台事务管理类,那么这两个事务就会被外面的事务进行统一的管理。那么这两个事务就具有了原子类的特性。当第一个事务执行后不会提交,中间代码出现异常,会将事务全部回滚。至于前面介绍了这么方法和属性,这些都是有默认值的。
另外一个需要注意点:事务管理器是需要一个连接池的。很好理解为什么需要连接池,你连会话对象都没有,你怎么将这些事务进行提交。事务管理器会自动从连接池中获取连接对事务进行提交。

现在我们思考,如果多个方法都需要对事务进行管理,那么这些代码都是固定统一的。所以我们可以使用AOP将这些共性的代码统一抽取出来。

管理事务-----AOP改造编程事务

配置AOP通知类,并注入dataSource

<bean id="txAdvice" class="com.xiao.aop.TxAdvice">
	<property name="dataSource" ref="dataSource"/>
</bean>

使用环绕通知将通知类织入到原始业务对象执行过程中

<bean id="txAdvice" class="com.MyDemo.aop.TxAdvice">
    <property name="dataSource" ref="dataSource"/>
</bean
<aop:config>
    <aop:pointcut id="pt" expression="execution(* *..transfer(..))"/>
    <aop:aspect ref="txAdvice">
    	<aop:around method="tx" pointcut-ref="pt"/>
	</aop:aspect>
</aop:config>

在重复代码中,我们需要创建 PlatformTransactionManager (平 台事务管理类),所以我们使用Bean标签进行单独配置。

管理事务-----声明式事务(XML)

spring见这些代码都这么固定,所以它定义了一套标签,使用这些标签来带体这些固定重复的代码,具体实现有spring帮你完成。

使用tx命名空间配置事务专属通知类

<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
</bean>

    <!--定义事务管理的通知类-->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <!--定义控制的事务-->
        <tx:attributes>
            <tx:method name="*" read-only="false"/>
            <tx:method name="get*" read-only="true"/>
            <tx:method name="find*" read-only="true"/>
            <tx:method name="a" read-only="false" propagation="REQUIRED"/>
            <tx:method name="b" read-only="false" propagation="NEVER"/>

            <tx:method
                name="transfer"
                read-only="false"
                timeout="-1"
                isolation="DEFAULT"
                no-rollback-for=""
                rollback-for=""
                propagation="REQUIRED"
                />
            <!--<tx:method name="transfer" read-only="false"/>-->
        </tx:attributes>
    </tx:advice>

    <aop:config>
        <aop:pointcut id="pt" expression="execution(* com.xiao.service.*Service.*(..))"/>
        <aop:pointcut id="pt2" expression="execution(* com.xiao.dao.*.b(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pt2"/>
    </aop:config>

在重复代码中,我们需要创建 PlatformTransactionManager (平台事务管理类),所以我们使用Bean标签进行单独配置。

tx:advice 标签就相当于我们的通知类,将通知类的代码全部由标签代替,spring看见这些标签就知道我们要进行事务的管理。

transaction-manager=“txManager” 但是平台事务管理类,这里还是需要我们自己创建传进去,因为PlatformTransactionManager是一个接口,不同的数据库有不同的实现类,所以需要我们自己手动指定,其他两个接口TransactionDefinition, TransactionStatus 都是固定的,就由spring帮我们进行处理了。

<tx:method
name="*"    待添加事务的方法名表达式(支持*号通配符),例如get* 、* 、……
read-only="false"   设置事务的读写属性,true为只读,false为读写
timeout="-1"		设置事务超时时长,单位秒
isolation="DEFAULT"		设置事务隔离级别,该隔离级设定是基于Spring的设定,非数据库端
no-rollback-for=""		设置事务中不回滚的异常,多个异常间使用,分割
rollback-for=""			设置事务中必回滚的异常,多个异常间使用,分割
propagation="REQUIRED"   设置事务的传播行为
/>

这个配置语句的作用等同于

//创建事务定义对象
 TransactionDefinition td = new DefaultTransactionDefinition();

这个对象的方法,现在转换为标签属性进行设置。

只是我们在前面没有进行设置,采用的默认设置,而且以后基础编程式事务用的比较少。你不设置也会有默认值

name 属性,具体指定哪些方法进行事务管理。在原有的切入点上进行过滤。因为原有的挂载点可能会统一挂载其他共性方法,但是里面个别方法才需要对事务进行管理

事务传播行为

在这里插入图片描述

在这里插入图片描述

设置多个事务之间对 事务平台管理器的态度,我是用你的还是使用自己的。默认REQUIRED

管理事务-----声明式事务(注解+XML)

名称:@Transactional
类型:方法注解,类注解,接口注解
位置:方法定义上方,类定义上方,接口定义上方
作用:设置当前类/接口中所有方法或具体方法开启事务,并指定相关事务属性
范例:

@Transactional(
    readOnly = false,
    timeout = -1,
    isolation = Isolation.DEFAULT,
    rollbackFor = {ArithmeticException.class, IOException.class},
    noRollbackFor = {},
    propagation = Propagation.REQUIRES_NEW
)

将tx标签和aop标签全部变成注解进行声明

但是需要xml配置文件声明,让spring认识这些注解

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

那么我们现在只需要把这个注解加载需要进行事务管理的方法上,我们之前方法的多事务就会由spring进行统一的管理

纯注解

名称:@EnableTransactionManagement
类型:类注解
位置:Spring注解配置类上方
作用:开启注解驱动,等同XML格式中的注解驱动
范例:

@Configuration
@ComponentScan("com.xiao")
@PropertySource("classpath:jdbc.properties")
@Import({JDBCConfig.class,MyBatisConfig.class,TransactionManagerConfig.class})
@EnableTransactionManagement
public class SpringConfig {
}
public class TransactionManagerConfig {
    @Bean
    public PlatformTransactionManager getTransactionManager(@Autowired DataSource dataSource){
   	 	return new DataSourceTransactionManager(dataSource);
    }
}
public interface AccountService {
    @Transactional
    public void transfer(String outName, String inName, Double money);

}

我们一般会加到 需要对多个事务进行统一管理方法的接口方法上,那么实现类的方法也会进行事务的统一管理,因为当你实现类更换,那么注解不需要进行更改。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值