前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring--事务管理

Spring--事务管理

作者头像
aruba
发布2022-05-20 09:12:52
2700
发布2022-05-20 09:12:52
举报
文章被收录于专栏:android技术

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

事务的特性也称为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项目配置

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

代码语言:javascript
复制
        <!--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>

准备数据库配置文件:

代码语言:javascript
复制
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配置文件:

代码语言:javascript
复制
<?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>

准备数据库表:

代码语言:javascript
复制
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接口和实现类:

代码语言:javascript
复制
public interface UserMapper {
    int updateMoney(int id, int money);
}
代码语言:javascript
复制
@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接口和实现类:

代码语言:javascript
复制
public interface UserService {
    int transMoney(int from, int to, int money);
}
代码语言:javascript
复制
@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;
    }
}

测试方法:

代码语言:javascript
复制
    @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事务管理

配置文件中新增事务配置:

代码语言:javascript
复制
<?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注解开启事务:

代码语言:javascript
复制
@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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-05-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、Spring项目配置
  • 二、代码准备
  • 三、Spring事务管理
  • 四、@Transactiona注解参数
    • propagation参数:
      • isolation参数:
        • 其他参数:
        • 项目地址:
        相关产品与服务
        数据库
        云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档