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>
相关推荐
绳全9 分钟前
OAuth2资源服务器白名单接口带token被拦截
java·服务器·spring
LuckyLay1 小时前
Spring学习笔记_38——@RequestParam
笔记·学习·spring·param·request
小林学习编程9 小时前
从零开始理解Spring Security的认证与授权
java·后端·spring
feilieren10 小时前
SpringBoot 2.x 整合 Redis
java·开发语言·spring
阑梦清川10 小时前
SpringMVC案例学习(二)--表白墙/图书管理系统1.0版本
spring·mvc·springboot·案例
武子康11 小时前
Java-05 深入浅出 MyBatis - 配置深入 动态 SQL 参数、循环、片段
java·sql·设计模式·架构·mybatis·代理模式
2的n次方_13 小时前
MyBatis——#{} 和 ${} 的区别和动态 SQL
数据库·sql·mybatis
疯一样的码农15 小时前
Spring Security PasswordEncoder接口(密码编码)
java·spring·spring security
HaiFan.15 小时前
Spring MVC
java·spring·mvc
荆州克莱16 小时前
Redis | Redis常用命令及示例总结(API)
spring boot·spring·spring cloud·css3·技术