Spring之事务(一)

这篇博客主要介绍了Spring框架中的事务管理,包括事务的基本概念、特性,以及JDBC的简述。在具体操作部分,作者详细讲解了如何进行准备工作,如添加依赖、配置Spring,创建数据库表,并演示了使用JdbcTemplate进行CRUD操作。接着,文章重点讨论了基于注解的声明式事务管理,包括在beans.xml中添加配置、创建相关组件,并通过@Transactional注解实现事务管理。最后,通过一个例子展示了事务未成功提交的情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Spring之事务(一)

前言

事务的基本概念

一组操作数据库的操作序列。这些操作具有原子性,要么全部执行,要么全部不执行,是一个不可分割的执行单位。

事务的特性:

  • 原子性:~ ~ ~ ~ ~
  • 一致性:执行事务前后数据库都处于一致的状态。如果事务提交成功,数据库更新成功,处于有效状态。如 果事务更新失败,则回滚到事务提交前状态。
  • 隔离性:两个事务之间不会相互干扰。(并发事务,就是两个并发事务之间不相互干扰)
  • 持久性:事务一但成功提交,数据库保存的就是数据库更新后的数据。

编程时事务:自己写的事务

声明式事务:框架对固定代码封装,通过框架实现功能,我们只需要在配置文件中进行简单的配置即可。

JDBC简述

我们通过java代码对数据库进行操作(一般是以事务提交)是调用java提供的API——JDBC,JDBC是sun公司定义的一组数据库操作的规则,各个数据库公司根据这套规则制定配套的jar包。

javaWeb中原生调用JDBC的代码太过于繁琐,所以在javaSpring框架中对JDBC进行了封装,spring提供了一个实用类——JdbcTemplate。它简化了使用 JDBC(Java 数据库连接)进行数据库操作的过程。它是一个在 Java 应用程序中使用 JDBC 访问关系型数据库的工具类,提供了很多便利的方法和模板代码,可以简化数据库访问的代码和开发工作。它支持各种主流数据库,如 Oracle、MySQL、SQL Server 等,并提供了丰富的功能,如批处理操作、事务管理、参数绑定等。

具体操作

​ 主要叙述的是spring框架后封装的操作,因为正在学这个。前面的javaWeb的JDBC操作,等我有空了再给它补上。

一、准备工作

老规矩先加入依赖。

1、加入依赖
<!--spring jdbc  Spring 持久化层支持jar包-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>6.0.2</version>
    </dependency>
    <!-- MySQL驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.30</version>
    </dependency>
    <!-- 数据源 -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.2.15</version>
    </dependency>

chatGpt是真的好用,直接搜相关依赖,然后CV就可以了。比我自己检索整理出来的依赖文件还省事儿。就是有时候容易一本正经的胡说八道,上午问Junit相关注解,他把切面的相关的注解给我了。

2、准备spring配置文件

bean.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: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/context
       http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 导入外部属性文件 -->
    <context:property-placeholder location="classpath:jdbc.properties" />
    
    <!-- 配置数据源 -->
    <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="url" value="${jdbc.url}"/>
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="username" value="${jdbc.user}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
    
    <!-- 配置 JdbcTemplate -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!-- 装配数据源 -->
        <property name="dataSource" ref="druidDataSource"/>
    </bean>

</beans>
3、准备个数据库表

Navicat for MySQL可视化操作确实不错,比我上学的时候学的在shell中操作可好太多了。

CREATE DATABASE `spring`;

use `spring`;

CREATE TABLE `t_emp` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) DEFAULT NULL COMMENT '姓名',
  `age` int(11) DEFAULT NULL COMMENT '年龄',
  `sex` varchar(2) DEFAULT NULL COMMENT '性别',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
4、实现CRUD

CV一个GPT的CRUD的介绍,CRUD操作可以定义为:

  • Create:创建新的数据记录或对象并将其添加到数据库或存储中。
  • Read:从数据库或存储中检索数据记录或对象。
  • Update:更新现有数据记录或对象的信息,并将其保存回数据库或存储中。
  • Delete:从数据库或存储中删除现有数据记录或对象。

这四个操作是许多应用程序的核心部分,它们可以用于管理和操作数据,让应用程序能够有效地管理和使用数据

​ 为什么把这段代码放在准备环境中,因为这段代码并不是事务,只是教会我正确使用spring框架对数据库增删改查。

先来个JdbcTemplate的测试类

@SpringJUnitConfig(locations = "classpath:beans.xml")
public class JDBCTemplateTest {
//字段注入
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
}

在实际应用中,建议尽可能使用构造函数注入或setter方法注入,而避免使用字段注入。改为下列代码

@SpringJUnitConfig(locations = "classpath:beans.xml")
public class JDBCTemplateTest {

    
    private JdbcTemplate jdbcTemplate;
    
    //setter方法注入
    @Autowired
    public void setPeopleDao(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

}

setter注入一般是选择配置,用构造器就是强配置

1、INSERT
@Test
//测试增删改功能
public void testUpdate(){
    //添加功能
	String sql = "insert into t_emp values(null,?,?,?)";
	int result = jdbcTemplate.update(sql, "张三", 23, "男");
    
    //修改功能
	//String sql = "update t_emp set name=? where id=?";
    //int result = jdbcTemplate.update(sql, "张三atguigu", 1);

    //删除功能
	//String sql = "delete from t_emp where id=?";
	//int result = jdbcTemplate.update(sql, 1);
}
2、SELECT
//返回对象类
public class Emp {

    private Integer id;
    private String name;
    private Integer age;
    private String sex;

    //生成get和set方法
    //......

    @Override
    public String toString() {
        return "Emp{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                '}';
    }
}

//查询:返回对象
@Test
public void testSelectObject() {
    //写法一
//        String sql = "select * from t_emp where id=?";
//        Emp empResult = jdbcTemplate.queryForObject(sql,
//                (rs, rowNum) -> {
//                    Emp emp = new Emp();
//                    emp.setId(rs.getInt("id"));
//                    emp.setName(rs.getString("name"));
//                    emp.setAge(rs.getInt("age"));
//                    emp.setSex(rs.getString("sex"));
//                    return emp;
//                }, 1);
//        System.out.println(empResult);

    //写法二
    String sql = "select * from t_emp where id=?";
    Emp emp = jdbcTemplate.queryForObject(sql,
                  new BeanPropertyRowMapper<>(Emp.class),1);
    System.out.println(emp);
}

@Test
//查询多条数据为一个list集合
public void testSelectList(){
    String sql = "select * from t_emp";
    List<Emp> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Emp.class));
    System.out.println(list);
}

@Test
//查询单行单列的值
public void selectCount(){
    String sql = "select count(id) from t_emp";
    Integer count = jdbcTemplate.queryForObject(sql, Integer.class);
    System.out.println(count);
}

二、实现

主要是介绍声明式事务,包括两种:基于注解的声明式~ 和 基于XML~

基于注解~
1、在beans.xml添加配置
<!--扫描组件-->
<context:component-scan base-package="com.lyz.spring6"></context:component-scan>
2、创建表
CREATE TABLE `t_book` (
  `book_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `book_name` varchar(20) DEFAULT NULL COMMENT '图书名称',
  `price` int(11) DEFAULT NULL COMMENT '价格',
  `stock` int(10) unsigned DEFAULT NULL COMMENT '库存(无符号)',
  PRIMARY KEY (`book_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
insert  into `t_book`(`book_id`,`book_name`,`price`,`stock`) values (1,'斗破苍穹',80,100),(2,'斗罗大陆',50,100);
CREATE TABLE `t_user` (
  `user_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `username` varchar(20) DEFAULT NULL COMMENT '用户名',
  `balance` int(10) unsigned DEFAULT NULL COMMENT '余额(无符号)',
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
insert  into `t_user`(`user_id`,`username`,`balance`) values (1,'admin',50);
3、创建组件

创建接口BookDao:

package com.atguigu.spring6.dao;
public interface BookDao {
    Integer getPriceByBookId(Integer bookId);

    void updateStock(Integer bookId);

    void updateBalance(Integer userId, Integer price);
}

创建实现类BookDaoImpl:

package com.atguigu.spring6.dao.impl;
@Repository
public class BookDaoImpl implements BookDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public Integer getPriceByBookId(Integer bookId) {
        String sql = "select price from t_book where book_id = ?";
        return jdbcTemplate.queryForObject(sql, Integer.class, bookId);
    }

    @Override
    public void updateStock(Integer bookId) {
        String sql = "update t_book set stock = stock - 1 where book_id = ?";
        jdbcTemplate.update(sql, bookId);
    }

    @Override
    public void updateBalance(Integer userId, Integer price) {
        String sql = "update t_user set balance = balance - ? where user_id = ?";
        jdbcTemplate.update(sql, price, userId);
    }
}

创建接口BookService:

package com.atguigu.spring6.service;
public interface BookService {
    void buyBook(Integer bookId, Integer userId);
}

创建实现类BookServiceImpl:

package com.atguigu.spring6.service.impl;
@Service
public class BookServiceImpl implements BookService {

    @Autowired
    private BookDao bookDao;

    @Override
    public void buyBook(Integer bookId, Integer userId) {
        //查询图书的价格
        Integer price = bookDao.getPriceByBookId(bookId);
        //更新图书的库存
        bookDao.updateStock(bookId);
        //更新用户的余额
        bookDao.updateBalance(userId, price);
    }
}

创建BookController:

package com.atguigu.spring6.controller;

@Controller
public class BookController {

    @Autowired
    private BookService bookService;

    public void buyBook(Integer bookId, Integer userId){
        bookService.buyBook(bookId, userId);
    }
}
4、加入事务
①添加事务配置

在spring配置文件中引入tx命名空间

<?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: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/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">

在Spring的配置文件中添加配置:

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="druidDataSource"></property>
</bean>

<!--
    开启事务的注解驱动
    通过注解@Transactional所标识的方法或标识的类中所有的方法,都会被事务管理器管理事务
-->
<!-- transaction-manager属性的默认值是transactionManager,如果事务管理器bean的id正好就是这个默认值,则可以省略这个属性 -->
<tx:annotation-driven transaction-manager="transactionManager" />
②添加事务注解

因为service层表示业务逻辑层,一个方法表示一个完成的功能,因此处理事务一般在service层处理

在BookServiceImpl的buybook()添加注解@Transactional

③观察结果

由于使用了Spring的声明式事务,更新库存和更新余额都没有执行

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值