Spring的事务

1. Spring中的事务控制方式

Spring 的事务控制可以分为编程式事务控制和声明式事务控制.
 
编程式
 
开发者直接把事务的代码和业务代码耦合到一起,在实际开发中不用。
 
声明式
 
开发者采用配置的方式来实现的事务控制,业务代码与事务代码实现解耦合,使用的 AOP 思想。
 

2. 编程式事务控制相关对象【了解】

2.1 PlatformTransactionManager

PlatformTransactionManager接口,是spring的事务管理器,里面提供了我们常用的操作事务的方法。

方法

说明

TransactionStatus getTransaction(TransactionDefinition definition);

获取事务的状态信息

void commit(TransactionStatus status)

提交事务

void rollback(TransactionStatus status)

回滚事务

注意:

  • PlatformTransactionManager 是接口类型,不同的 Dao 层技术则有不同的实现类。

 

  • Dao层技术是jdbcTemplatemybatis时: DataSourceTransactionManager

 

  • Dao层技术是hibernate时:

HibernateTransactionManager

 

  • Dao层技术是JPA时:

JpaTransactionManager

2.2 TransactionDefinition

TransactionDefinition接口提供事务的定义信息(事务隔离级别、事务传播行为等等)

方法

说明

int getIsolationLevel()

获得事务的隔离级别

int getPropogationBehavior()

获得事务的传播行为

int getTimeout()

获得超时时间

boolean isReadOnly()

是否只读

(1)事务隔离级别

设置隔离级别,可以解决事务并发产生的问题,如脏读、不可重复读和虚读(幻读)。

(2)事务传播行为

事务传播行为指的就是当一个业务方法【被】另一个业务方法调用时,应该如何进行事务控制。

参数

说明

 

REQUIRED

如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选择(默认值)

SUPPORTS

支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)

MANDATORY

使用当前的事务,如果当前没有事务,就抛出异常

REQUERS_NEW

新建事务,如果当前在事务中,把当前事务挂起

NOT_SUPPORTED

以非事务方式执行操作,如果当前存在事务,就把当前事务挂起

NEVER

以非事务方式运行,如果当前存在事务,抛出异常

 

NESTED

如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行

REQUIRED 类似的操作

  • read-only(是否只读):建议查询时设置为只读

 

  • timeout(超时时间):默认值是-1,没有超时限制。如果有,以秒为单位进行设置

2.3 TransactionStatus

TransactionStatus 接口提供的是事务具体的运行状态。

方法

说明

boolean isNewTransaction()

是否是新事务

boolean hasSavepoint()

是否是回滚点

boolean isRollbackOnly()

事务是否回滚

boolean isCompleted()

事务是否完成

可以简单的理解三者的关系:事务管理器通过读取事务定义参数进行事务管理,然后会产生一系列的事  务状态。

2.4 代码实现

(1)配置文件

(2)业务层代码

@Service
public class AccountServiceImpl implements AccountService { @Autowired
private AccountDao accountDao;

@Autowired
private PlatformTransactionManager transactionManager;

@Override
public void transfer(String outUser, String inUser, Double money) {
// 创建事务定义对象
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
// 设置是否只读,false支持事务
def.setReadOnly(false);
// 设置事务隔离级别,可重复读mysql默认级别
def.setIsolationLevel(TransactionDefinition.ISOLATION_REPEATABLE_READ);
// 设置事务传播行为,必须有事务
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
// 配置事务管理器
TransactionStatus status = transactionManager.getTransaction(def);

try {
// 转账
accountDao.out(outUser, money); accountDao.in(inUser, money);

// 提交事务
transactionManager.commit(status);
} catch (Exception e) { e.printStackTrace();
// 回滚事务
transactionManager.rollback(status);
}
}
}

2.5 知识小结

Spring中的事务控制主要就是通过这三个API实现的。

  • PlatformTransactionManager 负责事务的管理,它是个接口,其子类负责具体工作

 

  • TransactionDefinition 定义了事务的一些相关参数

 

  • TransactionStatus 代表事务运行的一个实时状态

理解三者的关系:事务管理器通过读取事务定义参数进行事务管理,然后会产生一系列的事务状态。

3. 基于XML的声明式事务控制【重点】

Spring 配置文件中声明式的处理事务来代替代码式的处理事务。底层采用AOP思想来实现的。

声明式事务控制明确事项:

  • 核心业务代码(目标对象) (切入点是谁?)
  • 事务增强代码(Spring已提供事务管理器))(通知是谁?) 切面配置(切面如何配置?)

3.1 快速入门

需求

使用spring声明式事务控制转账业务。

步骤分析

  1. 引入tx命名空间

 

  1. 事务管理器通知配置

 

  1. 事务管理器AOP配置

 

  1. 测试事务控制转账业务代码

(1)引入tx命名空间

<?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:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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/tx
		http://www.springframework.org/schema/tx/spring-tx.xsd
		http://www.springframework.org/schema/aop
		http://www.springframework.org/schema/aop/spring-aop.xsd
">
</beans>

(2)事务管理器通知配置

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

    <!--事务的注解支持-->
  <!--  <tx:annotation-driven/>-->

    <!--通知增强-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--定义事务的一些属性 * 表示当前任意名称的方法都走默认配置-->
        <!--
            name: 切点方法名称
            isolation:事务的隔离级别
            propagation:事务的传播行为
            read-only:是否只读
            timeout:超时时间
        -->
        <tx:attributes>
            <tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" timeout="-1"/>
            <!--**CRUD常用配置**	-->
            <tx:method name="save*" propagation="REQUIRED"/>
            <tx:method name="delete*" propagation="REQUIRED"/>
            <tx:method name="update*" propagation="REQUIRED"/>
            <tx:method name="find*" read-only="true"/>
            <tx:method name="*"/>

        </tx:attributes>
    </tx:advice>

(3)事务管理器AOP配置

 <!--aop配置:配置切面-->
    <aop:config>
        <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.lagou.servlet.impl.AccountServiceImpl.*(..))"/>
    </aop:config>

(4)测试事务控制转账业务代码

package com.lagou.test;

import com.lagou.config.SpringConfig;
import com.lagou.servlet.AccountSerivce;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration({"classpath:applicationContext.xml"})
@ContextConfiguration(classes = SpringConfig.class)
public class AccountServiceImplTest {

    @Autowired
    private AccountSerivce accountSerivce;

    @Test
    public void testTransfer(){
        accountSerivce.transfer("tom","jerry",100d);
    }


}

3.2 事务参数的配置详解

<tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" timeout="-1" read-only="false"/>

 

  • name:切点方法名称

 

  • isolation:事务的隔离级别

 

  • propogation:事务的传播行为

 

  • timeout:超时时间

 

  • read-only:是否只读

CRUD常用配置

<tx:attributes>

<tx:method name="save*" propagation="REQUIRED"/>

<tx:method name="delete*" propagation="REQUIRED"/>

<tx:method name="update*" propagation="REQUIRED"/>

<tx:method name="find*" read-only="true"/>

<tx:method name="*"/>

</tx:attributes>

3.3 知识小结

  • 平台事务管理器配置

 

  • 事务通知的配置

 

  • 事务aop织入的配置

4. 基于注解的声明式事务控制【重点】

4.1 常用注解

步骤分析

  1. 修改service层,增加事务注解
  2. 修改spring核心配置文件,开启事务注解支持

(1)修改service层,增加事务注解

package com.lagou.servlet.impl;

import com.lagou.dao.AccountDao;
import com.lagou.servlet.AccountSerivce;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
public class AccountServiceImpl implements AccountSerivce {

    @Autowired
    private AccountDao accountDao;


    //@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ,timeout = -1,readOnly = false)
    public void transfer(String outUser, String inUser, Double money) {

        //调用dao的out及in方法
        accountDao.out(outUser,money);

        int i = 1/0;

        accountDao.in(inUser,money);
    }
}

(2)修改spring核心配置文件,开启事务注解支持

<?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:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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/tx
		http://www.springframework.org/schema/tx/spring-tx.xsd
		http://www.springframework.org/schema/aop
		http://www.springframework.org/schema/aop/spring-aop.xsd
">

   <!-- IOC注解扫描-->
    <context:component-scan base-package="com.lagou"></context:component-scan>


    <!--引入properties-->
   <!-- <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>-->


    <!--dataSource-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>


    <!--jdbcTemplate-->
<!--    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <constructor-arg name="dataSource" ref="dataSource"/>
    </bean>-->



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

    <!--事务的注解支持-->
  <tx:annotation-driven/>

    <!--通知增强-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--定义事务的一些属性 * 表示当前任意名称的方法都走默认配置-->
        <!--
            name: 切点方法名称
            isolation:事务的隔离级别
            propagation:事务的传播行为
            read-only:是否只读
            timeout:超时时间
        -->
        <tx:attributes>
            <tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" timeout="-1"/>
            <!--**CRUD常用配置**	-->
            <tx:method name="save*" propagation="REQUIRED"/>
            <tx:method name="delete*" propagation="REQUIRED"/>
            <tx:method name="update*" propagation="REQUIRED"/>
            <tx:method name="find*" read-only="true"/>
            <tx:method name="*"/>

        </tx:attributes>
    </tx:advice>


    <!--aop配置:配置切面-->
    <aop:config>
        <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.lagou.servlet.impl.AccountServiceImpl.*(..))"/>
    </aop:config>






</beans>

4.2 纯注解

核心配置类

package com.lagou.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;

@Configuration  // 声明该类为核心配置类
@ComponentScan("com.lagou")  // 包扫描
@Import(DataSourceConfig.class) //导入其他配置类
@EnableTransactionManagement //事务的注解驱动
public class SpringConfig {

    @Bean
    public JdbcTemplate getJdbcTemplate(@Autowired DataSource dataSource){
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
        return jdbcTemplate;
    }

    @Bean
    public PlatformTransactionManager getPlatformTransactionManager(@Autowired DataSource dataSource){
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(dataSource);
        return dataSourceTransactionManager;
    }


}
package com.lagou.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;

import javax.sql.DataSource;

@PropertySource("classpath:jdbc.properties") //引入properties文件
public class DataSourceConfig {

    @Value("${jdbc.driverClassName}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;



    @Bean //会把当前方法的返回值对象放进IOC容器中
    public DataSource getDataSource(){

        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName(driver);
        druidDataSource.setUrl(url);
        druidDataSource.setUsername(username);
        druidDataSource.setPassword(password);
        return druidDataSource;
    }



}

4.3 知识小结

  • 平台事务管理器配置(xml、注解方式)

 

  • 事务通知的配置(@Transactional注解配置)

 

  • 事务注解驱动的配置 <tx:annotation-driven/>@EnableTransactionManagement

 

节选自拉钩教育JAVA系列课程

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值