IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Spring之(十一)Spring事务管理机制 -> 正文阅读

[Java知识库]Spring之(十一)Spring事务管理机制

十一、Spring事务管理机制

在spring中事务是自动提交的,但是我们在操作数据的时候,总有些业务流程需要事务控制。在实际开发中,操作数据库时还会涉及到事务管理的问题,为此Spring提供了专门用于事务处理的API,Spring事务管理简化了传统的事务管理流程,并在一定程度上减少了开发者的工作量。

在项目中,业务层(Service层)既是处理业务的地方,业务层编写又是管理数据库事务的地方,要对事务进行测试,首先创建业务层,并在业务层编写相关的业务代码。

以银行转账为例:张三账户上有100块钱,李四账户上有1块钱,当张三账户向李四账户转账时,在没有事务管理的情况下,转账双方无论转出和转入是否同时成功,这个程序都会运行,这样就导致了张三账户上的钱没有减少而李四账户上的钱增加了,很明显,吃亏的只能是银行,所以银行在做管理系统的时候,都会考虑到任何业务的执行情况,添加事务管理机制,并且配置正确,那么在转账时,只要有任何一方在执行业务的时候出现异常,事务立马回滚,保证事务同事成功或失败!

11.1 事务管理的核心接口

  • PlatformTransactionManager
    PlatformTransactionManager接口是Spring提供的平台事务管理器,主要用于管理事务。
  1. 事务操作的方法:
    • TransactionStatus getTransaction(TransactionDefinition definition):用于获取事务状态信息;
    • void commit(TransactionStatus status):用于提交事务
    • void rollback(TransactionStatus status):用于回滚事务
  2. 接口的不同实现类:
    • org.springframework.jdbc.datasource.DataSourceTransactionManager:用于配置JDBC数据源的事务管理器;
    • org.springframework.orm.hibernate4.HibernateTransactionManager:用于配置Hibernate的事务管理器;
    • org.springframework.transaction.jta.JtaTransactionManager:用于配置全局事务管理器。
  • TransactionDefinition
    TransactionDefinition接口是事务定义(描述)的对象,该对象中定义了事务规则,并提供了获取事务相关信息的方法:
    • String getName():获取事务对象名称;
    • int getIsolationLevel():获取事务的隔离级别;
    • int getPropagationBehavior():获取事务的传播行为;
    • int getTimeout():获取事务的超时时间;
    • boolean isReadOnly():获取事务是否只读。

这些方法中,事务的传播行为是指在同一个方法中,不同操作前后所使用的事务,传播行为有很多种:

参数描述
REQUIRED表示当前方法必须运行在一个事务环境当中,如果当前方法已处于事务环境中,则可以直接使用该方法;否则会开启一个新事物后执行该方法,这个可以作为首选的事务传播行为
SUPPORT如果当前方法处于事务环境中,则使用当前事务,否则不使用事务(即以非事务方法执行)
MANDATORY表示调用该方法的线程必须处于当前事务环境中,否则将抛出异常
REQUIRES_NEW要求方法在新的事务环境中执行。如果当前方法已在事务环境中,则先暂停当前事务,在启动新的事务后执行该方法;如果当前方法不在事务环境中,则会启动一个新的事务后执行方法
NOT_SUPPORTED不支持当前事务,总是以非事务状态执行,如果调用该方法的线程处于事务环境中,则先暂停当前事务,然后执行该方法
NEVER不支持当前事务,如果调用该方法的线程处于事务环境中,将抛异常
NESTED即使当前执行的方法处于事务环境中,依然会启动一个新的事务,并且方法在嵌套的事务里执行;即使当前执行的方法不在事务环境中,也会启动一个新事物,然后执行该方法

小贴士:
在事务管理过程中,传播行为可以控制是否需要创建事务以及如何创建事务,通常情况下,数据的查询不会影响源数据的改变,所以不需要进行事务管理,而对于数据的插入、更新和删除操作,必须进行事务管理。如果没有指定事件的传播行为,Spring默认传播行为是REQUIRED。

  • TransactionStatus
    TransactionStatus接口是事务的状态,它面搜狐了某一时间点上事务的状态信息,该接口中的方法:
    • void flush():刷新事务;
    • boolean hasSavepoint():获取是否存在保存点;
    • boolean isCompleted():获取事务是否完成;
    • boolean isNewTransaction():获取是否是新事物;
    • boolean isRollbackOnly():获取是否回滚;
    • boolean setRollbackOnly():设置事务回滚

11.2 事务管理的方式

Spring事务管理分为两种方式:一种是传统的编程式事务管理,另一种是声明式事务管理:

  • 编程式事务管理
    是通过编写代码实现的事务管理,包括定义事务的开始、正常执行后的事务提交和异常时的事务回滚。
  • 声明式事务管理
    是通过AOP技术实现的事务管理,其主要思想是将事务管理作为一个“切面”代码单独编写,然后通过AOP技术将事务管理的“切面”代码织入到业务目标类中。

声明式事务管理最大的优点在于开发者无需通过编程的方式来管理事务,只需在配置文件汇总进行相关的事务规则声明,就可以将事务规则应用到业务逻辑中。这使得开发人员可以更加专注于核心业务逻辑代码的编写,在一定程度上减少了工作量,提供了开发效率,所以在实际开发中,通常都推荐使用是鞥明是事务管理。

11.3 配置声明式事务的核心问题

对哪些方法,采取什么样的事务策略。

11.4 基于XML方式的声明式事务管理

基于XML方式的声明式事务管理是通过在配置文件中配置事务的相关声明来实现的。
在这里插入图片描述

以银行转账为例:

实体类Account.java

package cn.pojo;
public class Account implements  java.io.Serializable
{
    private  String accountid;
    private String accoutname;
    private float accountmoney;
    public String getAccountid() {
        return accountid;
    }
    public void setAccountid(String accountid) {
        this.accountid = accountid;
    }
    public String getAccoutname() {
        return accoutname;
    }
    public void setAccoutname(String accoutname) {
        this.accoutname = accoutname;
    }
    public float getAccountmoney() {
        return accountmoney;
    }
    public void setAccountmoney(float accountmoney) {
        this.accountmoney = accountmoney;
    }
}

dao层接口AccountMapper.java

package cn.dao;
import org.apache.ibatis.annotations.Param;

/**
 * 账户数据层访问接口
 */
public interface AccountMapper {
    /**
     * 转出(取款)
     * @param accountid 卡号id
     * @param money 交易金额
     * @return
     */
    int transMoney1(@Param("accountid")String accountid,@Param("money")float money);

    /**
     * 转入(存款)
     * @param accountid 卡号id
     * @param money 交易金额
     * @return
     */
    int transMoney2(@Param("accountid")String accountid,@Param("money")float money);
}

业务逻辑层接口及实现类AccountService.java和AccountServiceImpl.java

//接口AccountService.java
package cn.service;
import org.apache.ibatis.annotations.Param;
/**
 * 业务逻辑层接口
 */
public interface AccountService {
    /**
     * 转账
     * @param accountid1 交易账户1
     * @param accountid2 交易账户2
     * @param money      交易金额
     * @return
     */
    int transMoney(@Param("accountid1")String accountid1,@Param("accountid2")String accountid2,@Param("moeny")float money);
}
//实现类AccountServiceImpl.java
package cn.service.impl;
import cn.dao.AccountMapper;
import cn.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class AccountServiceImpl implements AccountService {
    @Autowired
    AccountMapper accountMapper;
    /**
     * 转账记录
     * @param accountid1 转出账户 张三
     * @param accountid2 转入账户 李四
     * @param money      交易金额
     * @return
     */
    @Override
    public int transMoney(String accountid1, String accountid2, float money) {
        int r1 = accountMapper.transMoney1(accountid1,money);
        int r2 = accountMapper.transMoney2(accountid2,money);
        return 1;
    }
}

MyBatis配置文件mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--设置MyBatis控制台输出为log4j-->
    <settings>
        <setting name="logImpl" value="log4j"/>
        <!--开启自动映射-->
        <setting name="autoMappingBehavior" value="FULL"/>
        <!--开启二级缓存-->
        <setting name="cacheEnabled" value="true"/>
    </settings>
    <typeAliases>
        <!--表示设置自动扫描的方式设置别名-->
        <package name="cn.pojo"/>
    </typeAliases>
</configuration>

Spring核心配置文件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:p="http://www.springframework.org/schema/p"
       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-3.2.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.2.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">
    <!--加载Oracle数据库的参数配置文件-->
    <context:property-placeholder location="database.properties"/>
    <!-- 配置数据源连接池 (使用alibaba提供的druid连接池——德鲁斯) -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <!--这一项可配可不配,如果不配置,druid会根据url自动识别dbType,然后选择相应的driverClassName-->
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
        <property name="filters" value="${jdbc.filters}"/>
        <property name="maxActive" value="${jdbc.maxActive}"/>
        <property name="initialSize" value="${jdbc.initialSize}"/>
        <property name="maxWait" value="${jdbc.maxWait}"/>
        <property name="minIdle" value="${jdbc.minIdle}"/>
        <property name="timeBetweenEvictionRunsMillis" value="${jdbc.timeBetweenEvictionRunsMillis}"/>
        <property name="minEvictableIdleTimeMillis" value="${jdbc.minEvictableIdleTimeMillis}"/>
        <property name="validationQuery" value="${jdbc.validationQuery}"/>
        <property name="testWhileIdle" value="${jdbc.testWhileIdle}"/>
        <property name="testOnBorrow" value="${jdbc.testOnBorrow}"/>
        <property name="testOnReturn" value="${jdbc.testOnReturn}"/>
        <property name="poolPreparedStatements" value="${jdbc.poolPreparedStatements}"/>
        <property name="maxPoolPreparedStatementPerConnectionSize"
                  value="${jdbc.maxPoolPreparedStatementPerConnectionSize}"/>
    </bean>
    <!--配置sqlSessionFactory工厂-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="configLocation" value="mybatis-config.xml"/>
    </bean>
    <!--指定MyBatis到哪个包下扫描映射文件-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="cn.dao"/>
    </bean>
    <!--指定spring要扫描的包-->
    <context:component-scan base-package="cn.service"/>
    <!--配置事务管理器   必须限定当转账双方都满足约束条件时才能转账成功!-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!--配置通知-->
    <tx:advice id="txAdvice">
        <tx:attributes>
            <!--propagation事务传播机制-->
            <tx:method name="trans*" propagation="REQUIRED"/>
            <tx:method name="save*" propagation="REQUIRED"/>
            <tx:method name="update*" propagation="REQUIRED"/>
            <tx:method name="add*" propagation="REQUIRED"/>
            <tx:method name="modify*" propagation="REQUIRED"/>
            <tx:method name="del*" propagation="REQUIRED"/>
            <tx:method name="remove*" propagation="REQUIRED"/>
            <tx:method name="get*" propagation="SUPPORTS"/>
            <tx:method name="find*" propagation="SUPPORTS"/>
            <tx:method name="search*" propagation="SUPPORTS"/>
        </tx:attributes>
    </tx:advice>
    <!--定义切面-->
    <aop:config>
        <!--切入点-->
        <aop:pointcut id="serviceMethod" expression="execution(* cn.service..*.*(..))"/>
        <!--通知-->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethod"/>
    </aop:config>
</beans>

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

11.5 基于Annotation方式的声明式事务

Spring的声明式事务管理还可以通过注解方式实现,这种方式下我们只需做两件事情:

  1. 在Spring容器中注册事务注解驱动;
  2. 在需要使用事务的Spring Bean类或者Bean类的方法上添加注解@Transactional。如果将注解添加在Bean类上,则表示事务的社会对整个Bean类的所有方法都起作用;如果将注解添加在Bean类的某个方法上,则表示事务的设置只对该方法有效。

在这里插入图片描述

在上述例子中,我们只需在xml文件中添加自动注解,在service中添加事务注解即可:

Spring核心配置文件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:p="http://www.springframework.org/schema/p"
       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-3.2.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.2.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">
    <!--加载Oracle数据库的参数配置文件-->
    <context:property-placeholder location="database.properties"/>
    <!-- 配置数据源连接池 (使用alibaba提供的druid连接池——德鲁斯) -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <!--这一项可配可不配,如果不配置,druid会根据url自动识别dbType,然后选择相应的driverClassName-->
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
        <property name="filters" value="${jdbc.filters}"/>
        <property name="maxActive" value="${jdbc.maxActive}"/>
        <property name="initialSize" value="${jdbc.initialSize}"/>
        <property name="maxWait" value="${jdbc.maxWait}"/>
        <property name="minIdle" value="${jdbc.minIdle}"/>
        <property name="timeBetweenEvictionRunsMillis" value="${jdbc.timeBetweenEvictionRunsMillis}"/>
        <property name="minEvictableIdleTimeMillis" value="${jdbc.minEvictableIdleTimeMillis}"/>
        <property name="validationQuery" value="${jdbc.validationQuery}"/>
        <property name="testWhileIdle" value="${jdbc.testWhileIdle}"/>
        <property name="testOnBorrow" value="${jdbc.testOnBorrow}"/>
        <property name="testOnReturn" value="${jdbc.testOnReturn}"/>
        <property name="poolPreparedStatements" value="${jdbc.poolPreparedStatements}"/>
        <property name="maxPoolPreparedStatementPerConnectionSize"
                  value="${jdbc.maxPoolPreparedStatementPerConnectionSize}"/>
    </bean>
    <!--配置sqlSessionFactory工厂-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="configLocation" value="mybatis-config.xml"/>
    </bean>
    <!--指定MyBatis到哪个包下扫描映射文件-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="cn.dao"/>
    </bean>
    <!--指定spring要扫描的包-->
    <context:component-scan base-package="cn.service"/>
    <!--配置事务管理器   必须限定当转账双方都满足约束条件时才能转账成功!-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    
    <!--开启Spring事务注解-->
    <tx:annotation-driven transaction-manager="transactionManager"/>
</beans>

业务层实现类AccountServiceImpl.java

package cn.service.impl;
import cn.dao.AccountMapper;
import cn.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional(propagation = Propagation.REQUIRED)
public class AccountServiceImpl implements AccountService {
    @Autowired
    AccountMapper accountMapper;

    /**
     * 转账记录
     * @param accountid1 转出账户 张三
     * @param accountid2 转入账户 李四
     * @param money      交易金额
     * @return
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public int transMoney(String accountid1, String accountid2, float money) {
        int r1 = accountMapper.transMoney1(accountid1,money);
        int r2 = accountMapper.transMoney2(accountid2,money);
        return 1;
    }
}

也就是说:
在这里插入图片描述
在这里插入图片描述

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-07-24 11:18:43  更:2021-07-24 11:18:46 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/18 17:53:08-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码