spring使用xml文件整合事务+druid+mybatis

1.事务

事务(Transaction)是数据库管理系统中的一个重要概念,它表示一组不可分割的操作序列,这些操作要么全部执行成功,要么全部不执行,以确保数据库从一个一致性状态转换到另一个一致性状态。事务具有以下四个基本特性,通常被称为ACID特性.

2. ACID特性

原子性(Atomicity):事务中的所有操作要么全部完成,要么全部不完成,不会结束在中间某个点。


一致性(Consistency) :事务必须使数据库从一个一致性状态转换到另一个一致性状态。也就是说,事务执行的结果必须符合所有预定义的规则和约束,例如数据的完整性和业务规则

比如:(转账:我有100 张三也有100,我和张三两个人的钱总数是200,这时张三转账给我50,张三只剩50,而我收款了有了150,那么我们两个人加起来的钱还是200,不管谁转给谁总数是不变的)


隔离性(Isolation) :并发执行的事务之间不会互相影响。事务的隔离性描述了多个事务并发执行时,事务之间的相互影响程度。不同的数据库系统可能提供不同级别的隔离性,例如读未提交(Read

Uncommitted)、读已提交(Read Committed)、可重复读(Repeatable

Read)和串行化(Serializable)。


持久性(Durability):一旦事务被提交,它对数据库所做的更改就是永久性的。即使系统发生故障,事务的结果也不会丢失。

3. 事务注解@Transactional

事务注解标注的地方:

1.类:当把@Transactional 注解放在类上时,表示所有该类的public方法都配置相同的事务属性信息,会导致事务控制的粒度太大,注解参数无法根据每个类方法的实际需求设置;因此,一般@Transactional注解都会直接添加的需要的方法上;

2. 方法: 当类配置了@Transactional,方法也配置了@Transactional,方法的事务会覆盖类的事务配置信息;

3. 接口:不推荐这种使用方法,因为一旦标注在Interface上并且配置了Spring AOP 使用CGLib动态代理,将会导致@Transactional注解失效;

4. @Transactional注解两个属性

rollbackFor属性:rollbackFor属性指定遇到什么类型的异常就进行回滚,默认是RuntimeException


propagation属性:propagation属性用于设置事务的椽笔级别,默认的级别就是REQUIRED

5. 事务的传播级别

在Spring框架中,事务管理是通过声明式事务管理来实现的,它允许我们以声明的方式配置事务。Spring事务的传播级别(Propagation
Levels)定义了事务的边界和范围,即当一个事务方法被另一个事务方法调用时,事务如何被传播。Spring定义了7种事务传播级别,它们分别如下:


  1. REQUIRED:表示当前方法必须在一个具有事务的上下文中运行,如有客户端有事务在进行,那么被调用端将在该事务中运行,否则的话重新开启一个事务。(如果被调用端发生异常,那么调用端和被调用端事务都将回滚)

  2. SUPPORTS:表示当前方法不必需要具有一个事务上下文,但是如果有一个事务的话,它也可以在这个事务中运行,没有事务则已非事务执行,就是纯方法

  3. MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,就抛出异常。表示当前方法必须在一个事务中运行,如果没有事务,将抛出异常

  4. REQUIRES_NEW:新建事务,如果当前存在事务,就把当前事务挂起。这是用于需要创建独立事务的方法。表示当前方法必须运行在它自己的事务中。一个新的事务将启动,而且如果有一个现有的事务在运行的话,则这个方法将在运行期被挂起,直到新的事务提交或者回滚才恢复执行。

  5. NOT_SUPPORTED:以非事务方式执行,如果当前存在事务,就把当前事务挂起。

  6. NEVER:以非事务方式执行,如果当前存在事务,就抛出异常。

  7. NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,行为与REQUIRED相同。表示如果当前方法正有一个事务在运行中,则该方法应该运行在一个嵌套事务中,被嵌套的事务可以独立于被封装的事务中进行提交或者回滚。如果封装事务存在,并且外层事务抛出异常回滚,那么内层事务必须回滚,反之,内层事务并不影响外层事务。如果封装事务不存在,则同PROPAGATION_REQUIRED的一样

6. 事务流程图

Spring AOP将通用的功能横向抽取出来作为切面,避免非业务代码侵入到业务代码中;通过@Transactional注解就能让Spring为我们管理事务,免去了重复的事务管理逻辑,减少对业务代码的侵入,让开发人员能够专注于业务层面开发;【这里我们知道事务时基于spring中的AOP(切面)进行实现的】

7. 必要依赖

xml 复制代码
 <dependency>
 			<!--mysql连接依赖-->
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <version>8.3.0</version>
        </dependency>

        <!-- druid连接池依赖 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.23</version>
        </dependency>
		
		<!--spring框架依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>6.1.12</version>
        </dependency>

		<!--spring框架操作使用jdb操作数据库依赖(包括了事务(spring-tx)依赖)-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>6.1.12</version>
        </dependency>
        
        <!--mybatis依赖-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.16</version>
        </dependency>
        
        <!-- spring整合mybatis插件依赖 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>3.0.4</version>
        </dependency>

		<!--lombox依赖-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.32</version>
        </dependency>

8. 整合(druid数据源,mybatis,事务)

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

    <!-- 扫描包 -->
    <context:component-scan base-package="edu.nf.ch03"/>

    <!-- 整合Druid数据源连接池 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <!-- 注入相关的连接属性 -->
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/demo?useSSL=false&amp;useUnicode=true&amp;characterEncoding=utf-8&amp;timeZone=Asia/Shanghai"/>
        <property name="username" value="root"/>
        <property name="password" value="1234"/>
        <!-- 连接池的属性配置-->
        <!-- 最大连接池数量-->
        <property name="maxActive" value="200"/>
        <!-- 初始化连接池的时候建立的连接个数-->
        <property name="initialSize" value="10"/>
        <!-- 连接池最小连接数-->
        <property name="minIdle" value="10"/>
        <!-- 获取连接的最大等待时间,单位:毫秒-->
        <property name="maxWait" value="2000"/>
        <!-- 检测连接是否有效-->
        <property name="testWhileIdle" value="true"/>
        <property name="testOnReturn" value="false"/>
        <!-- 用一条伪sql来检查连接-->
        <property name="validationQuery" value="select 1"/>
        <!-- 是否缓存PreparedStatement,mysql中建议关闭-->
        <property name="poolPreparedStatements" value="false"/>
    </bean>

    <!-- 整合mybatis, 目的就是将SqlSessionFactory纳入IOC容器,
    SqlSessionFactoryBean实现了Spring的FactoryBean接口,这样spring
    容器就可以通过接口的getObject方法得到SqlSessionFactory并纳入容器中
    -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 注入数据源连接池 -->
        <property name="dataSource" ref="dataSource"/>
        <!-- 实体类的别名-->
        <property name="typeAliasesPackage" value="edu.nf.ch03.entity"/>
        <!-- 指定mapper映射配置文件的路径-->
        <property name="mapperLocations" value="classpath:mappers/*.xml"/>
    </bean>
    <!-- 指定扫描的dao接口,这样就会基于动态代理在运行时创建Dao接口的代理实现,
     这个代理实现也会自动纳入Spring容器-->
    <mybatis:scan base-package="edu.nf.ch03.dao"/>

    <!-- 装配JDBC本地事务管理器 -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 注入数据源 -->
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- 启用事务注解处理器,transaction-manager引用JDBC事务管理器的id -->
    <tx:annotation-driven transaction-manager="txManager"/>
</beans>

9. 创建账户实体类(Account)

Account类

java 复制代码
/**
 * 账户实体类
 */
@Data
public class Account {
	// 账户id
    private Integer cardId;
    // 账户卡号
    private String cardNum;
    // 账户余额
    private BigDecimal balance;
}

10. 创建转账业务的接口(TransferService),并创建对应的实现类

TransferService接口

java 复制代码
public interface TransferService {

    /**
     * 检查账户余额
     * @param fromUser
     * @param money
     */
    void check(Account fromUser, BigDecimal money);

    /**
     * 转账
     * @param money 转账金额
     * @param formUser 转账人
     * @param toUser 接收人
     */
    void transfer(BigDecimal money, Account formUser, Account toUser);
}

TransferServiceImpl实现类

java 复制代码
/**
 * @author xiruo
 * 事务注解@Transactional可以声明在类上(类中的所有方法参与事务),
 * 也可以声明在方法上(当前方法参与事务);
 * rollbackFor属性指定遇到什么类型的异常就进行回滚,默认是RuntimeException
 * propagation属性用于设置事务的级别,默认的级别就是REQUIRED
 */
@Service("transferService")
@RequiredArgsConstructor
@Transactional(rollbackFor = RuntimeException.class,
        propagation = Propagation.REQUIRED)
public class TransferServiceImpl implements TransferService {

    private final AccountDao dao;

	// readOnly为true时,该方法为只读事务,引文查询不需要回滚,只要查询即可
	// 方法和类都标注@Transactional注解时,方法会覆盖类的事务
    @Transactional(readOnly = true)
    @Override
    public void check(Account fromUser, BigDecimal money) {
        //1. 获取转账人信息
        fromUser = dao.getOverByAccount(fromUser.getCardNum());
        //2. 检查转账人余额
        if(fromUser.getBalance().compareTo(money) < 0) {
            throw new RuntimeException("对不起,您的余额不足");
        }
    }

    @Override
    public void transfer(BigDecimal money, Account formUser, Account toUser) {
        //1. 获取转账人信息
        formUser = dao.getOverByAccount(formUser.getCardNum());
        //2. 获取接收人信息
        toUser = dao.getOverByAccount(toUser.getCardNum());
        //3. 转账人扣除转账金额
        formUser.setBalance(formUser.getBalance().subtract(money));
        dao.updateAccount(formUser);
        //4. 接收人添加转账金额
        toUser.setBalance(toUser.getBalance().add(money));
        dao.updateAccount(toUser);
        //引发异常
        //System.out.println(10/0);
    }
}

11. 创建AccountDao接口,以及对应的mapper.xml文件(AccountMapeer)

AccountDao接口

java 复制代码
public interface AccountDao {

    /**
     * 查询账户余额
     * @param cardNum 卡号
     * @return 账户信息
     */
    Account getOverByAccount(String cardNum);

    /**
     * 更新账户
     * @param account 账户信息
     */
    void updateAccount(Account account);
}

AccountMapeer

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="edu.nf.ch03.dao.AccountDao">

    <resultMap id="accountMap" type="edu.nf.ch03.entity.Account">
        <id property="cardId" column="card_id"/>
        <result property="cardNum" column="card_num"/>
        <result property="balance" column="balance"/>
    </resultMap>

    <select id="getOverByAccount" resultMap="accountMap">
        select card_id, card_num, balance from account_info where card_num = #{cardNum}
    </select>

    <update id="updateAccount">
        update account_info set balance = #{balance} where card_num = #{cardNum}
    </update>
</mapper>
相关推荐
BUG指挥官13 小时前
Sa-Token和Spring Security对比
java·后端·spring
geminigoth14 小时前
Spring AI Alibaba 入门开发一(备份)
java·人工智能·spring
xbgRS16 小时前
spring整合mybaits
java·spring·mybatis
ruleslol16 小时前
静态依赖注入 VS 动态依赖查询
spring
墨雨晨曦8819 小时前
Spring AI总结
java·人工智能·spring
用户31268748772021 小时前
Spring Boot 异常处理到底怎么玩的?从 DispatcherServlet 到全局兜底的全链路拆解
spring
不才不才不不才21 小时前
Spring 源码系列(17): HandlerMapping 与 HandlerAdapter 两大体系
java·后端·spring
(轻舟已过万重山)1 天前
第40章 Spring AI 实战:企业级 AI 应用架构
人工智能·spring·架构
vHelios1 天前
【电商项目】商品服务模块的问题解决与代码逻辑思考
java·sql·mybatis
敲个大西瓜1 天前
Mybatis核心面试题
mybatis