Spring--事务管理

事务简单来说,就是将多个操作成为一个工作单元,其中任何一个操作执行失败,都会回到工作单元之前的状态

事务的特性也称为ACID,原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability),真想吐槽谁发明的这些概念

事务的并发问题

  • 脏读 : 一个事务A访问数据,另一个事务B进行了修改,A重新访问获得了修改后的数据。我们希望A获得原来的数据
  • 不可重复读 : 一个事务A访问数据,另一个事务B进行了修改并提交,A重新访问获得了修改后的数据。我们希望A获得原来的数据
  • 幻读 : 一个事务A访问数据,另一个事务B进行了插入数据并提交,A重新访问数据得到了增加后的数据。我们希望A获得原来的数据

事务的隔离级别可以解决并发问题
从低到高依次为READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ以及SERIALIZABLE,隔离级别越低,越能支持高并发的数据库操作。

隔离级别脏读不可重复读幻读
READ UNCOMMITTED不解决不解决不解决
READ COMMITTED解决不解决不解决
REPEATABLE READ解决解决不解决
SERIALIZABLE解决解决解决

一般情况下,我们选择REPEATABLE READ,幻读不以解决

一、Spring项目配置

事务需要操作数据库,导入以下依赖:

<!--spring核心容器包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.5</version>
        </dependency>
        <!--spring切面包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.3.5</version>
        </dependency>
        <!--aop联盟包-->
        <dependency>
            <groupId>aopalliance</groupId>
            <artifactId>aopalliance</artifactId>
            <version>1.0</version>
        </dependency>
        <!--德鲁伊连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.10</version>
        </dependency>
        <!--mysql驱动-->
         <dependency>
             <groupId>mysql</groupId>
             <artifactId>mysql-connector-java</artifactId>
             <version>8.0.22</version>
         </dependency>
        <!--springJDBC包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.3.5</version>
        </dependency>
        <!--spring事务控制包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.3.5</version>
        </dependency>
        <!--spring orm 映射依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>5.3.5</version>
        </dependency>
        <!--Apache Commons日志包-->
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>

准备数据库配置文件:

jdbc_driver=com.mysql.cj.jdbc.Driver
jdbc_url=jdbc:mysql://127.0.0.1:3306/mydb?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
jdbc_username=root
jdbc_password=root

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:c="http://www.springframework.org/schema/c"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/util
       http://www.springframework.org/schema/util/spring-util.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
">
    <!--读取jdbc配置-->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!--配置连接池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="username" value="${jdbc_username}"></property>
        <property name="password" value="${jdbc_password}"></property>
        <property name="url" value="${jdbc_url}"></property>
        <property name="driverClassName" value="${jdbc_driver}"></property>
    </bean>

    <!--配置JDBCTemplate对象,并向里面注入DataSource-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--通过set方法注入连接池-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--多个包可以使用,分割-->
    <context:component-scan base-package="com.aruba"/>
</beans>

准备数据库表:

create table user(
id int primary key auto_increment,
name varchar(10) not null,
money int not null default 0
);

insert into user values(default,'zhangsan',2000);
insert into user values(default,'lisi',2000);

二、代码准备

创建Mapper接口和实现类:

public interface UserMapper {
    int updateMoney(int id, int money);
}
@Repository
public class UserMapperImpl implements UserMapper {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public int updateMoney(int id, int money) {
        String sql = "update user set money =money + ? where id =?";
        Object[] args = {money, id};
        return jdbcTemplate.update(sql, args);
    }
}

创建Service接口和实现类:

public interface UserService {
    int transMoney(int from, int to, int money);
}
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;

    @Override
    public int transMoney(int from, int to, int money) {
        userMapper.updateMoney(from,-money);
        userMapper.updateMoney(to,money);
        return 0;
    }
}

测试方法:

@org.junit.Test
    public void test() {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        UserService userService = applicationContext.getBean(UserService.class);
        userService.transMoney(5, 6, 200);
    }

重新查询下数据:

三、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:c="http://www.springframework.org/schema/c"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:util="http://www.springframework.org/schema/util"
       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://www.springframework.org/schema/util
       http://www.springframework.org/schema/util/spring-util.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
">
...

    <!--配置一个事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--将数据源注入事务管理器-->
        <property name="dataSource"  ref="dataSource"></property>
    </bean>
    <!--开启事务注解-->
    <tx:annotation-driven transaction-manager="transactionManager"/>

</beans>

Service层使用@Transactiona注解开启事务:

@Service
//@Transactional 所有方法开启事务
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;

    @Override
    @Transactional //该方法开启事务
    public int transMoney(int from, int to, int money) {
        userMapper.updateMoney(from, -money);
        int a = 1 / 0;//模拟一个异常
        userMapper.updateMoney(to, money);
        return 0;
    }
}

这边模拟一个异常

结果:

数据库中数据没有变化:

四、@Transactiona注解参数

propagation参数:
事务传播行为类型描述
PROPAGATION_REQUIRED如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。默认值
PROPAGATION_SUPPORTS支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY使用当前的事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
isolation参数:
事务隔离级别描述
DEFAULT使用数据库默认的事务隔离级别,MySQL默认REPEATABLE_READ,Oracle默认READ_COMMITTED
READ_UNCOMMITTED产生脏读,不可重复读和幻像读
READ_COMMITTED可以避免脏读出现,但是可能会出现不可重复读和幻读
REPEATABLE_READ可以防止脏读、不可重复读,但是可能出现幻读
SERIALIZABLE防止脏读、不可重复读外,还避免了幻像读
其他参数:
  • timeout : 指定事务在多长时间之内提交,如果不提交就会回滚
  • readOnly : 事务是否只能读取数据库的数据
  • rollbackFor : 当方法发生哪些异常时才会回滚
  • noRollbackFor : 当方法发生哪些异常时,不会回滚

项目地址:

https://gitee.com/aruba/spring-study.git

posted @   aruba_233  阅读(11)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
点击右上角即可分享
微信分享提示