手撕Spring5框架(九)事务管理

什么是事务?

事务是数据库操作最基本单元,逻辑上的一组操作,要么都成功,如果有一个失败所有操作都失败。

事务特性(ACID)

原子性:不可分割,要么成功,要么都失败。

一致性:操作之前和操作之后的总量是不变得。

隔离性:多事务间不影响。

持久性:事务提交后,表中数据发生持久变化。

事务操作

典型场景:银行转账

场景描述:小明去银行转账给小红100元,小明执行转账方法,方法执行后,小明的账户减少100元,小红的账户增加100元。

搭建事务操作的环境

  • 创建数据库表,并添加记录
DROP TABLE IF EXISTS `t_account`;
CREATE TABLE `t_account`  (
  `id` int(11) NOT NULL,
  `user_name` varchar(55) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '用户名',
  `money` decimal(11, 2) NULL DEFAULT NULL COMMENT '账户金额',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of t_account
-- ----------------------------
INSERT INTO `t_account` VALUES (1, '小明', 1000.00);
INSERT INTO `t_account` VALUES (2, '小红', 1000.00);

SET FOREIGN_KEY_CHECKS = 1;
  •  创建UserService接口和实现类、UserDao接口和实现类,配置Spring的XML文件
package org.learn.spring5.service;

public interface UserService {

    public void transferAccount();
}
package org.learn.spring5.service.impl;

import org.learn.spring5.dao.UserDao;
import org.learn.spring5.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;
    public void transferAccount() {
        //小明转账100,减少100元
        userDao.reduceMoney();
        //小红账户增加100元
        userDao.addMoney();
    }
}
package org.learn.spring5.dao.impl;

import org.learn.spring5.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

@Component
public class UserDaoImpl implements UserDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;
    public int reduceMoney() {
    String sql=" update t_account set money=money-100 where user_name ='小明'";
        Object[] args = {};
        int update = jdbcTemplate.update(sql, args);

        return update;
    }

    public int addMoney() {
        String sql=" update t_account set money=money+100 where user_name ='小红'";
        Object[] args = {};
        int update = jdbcTemplate.update(sql, args);


        return update;
    }
}
package org.learn.spring5.dao;

public interface UserDao {

    //账户金额减少的方法
    public int reduceMoney();
    //账户金额增加的方法
    public int addMoney();
}
package org.learn.spring5.dao.impl;

import org.learn.spring5.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

@Component
public class UserDaoImpl implements UserDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;
    public int reduceMoney() {
    String sql=" update t_account set money=money-100 where user_name ='小明'";
        Object[] args = {};
        int update = jdbcTemplate.update(sql, args);

        return update;
    }

    public int addMoney() {
        String sql=" update t_account set money=money+100 where user_name ='小红'";
        Object[] args = {};
        int update = jdbcTemplate.update(sql, args);


        return update;
    }
}
<?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 https://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="org.learn.spring5"></context:component-scan>
    <!--引入外部的配置文件-->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <!--配置数据库连接-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
        <property name="url" value="${prop.url}"></property>
        <property name="username" value="${prop.userName}"></property>
        <property name="password" value="${prop.password}"></property>
        <property name="driverClassName" value="${prop.driverClass}"></property>
    </bean>

    <!--JdbcTemplate对象创建,注入dataSource对象-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--注入dataSource对象-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

</beans>

从上面代码我们可以分析出,Service层用于处理业务,具体有两部操作,小明转账,账户减少100元,和小红账户增加100元

Dao层用于操作数据库,分别执行了小明账户减少100和小红账户增加100的数据操作,如果我们没有使用事务发生异常会发生什么问题呢?

假如在处理如上业务逻辑时1.小明转账,账户减少100,当执行完这步操作时,网络突然中断,没有执行下一步操作,这时候数据库会执

行小明账户减少100元的操作,结果是小明的账户减少100,而小红的账户金额没有发生变化。这种情况明显是错误的。正常情况应该是如果转账成功,小明账户减少100,小红账户增加100。如果发生异常,小明和小红的账户金额应该保持不变。如何处理这个问题呢?事务的引入就可以解决这个问题,让我们在回顾一下事务的定义:逻辑上的一组操作,要么都成功,如果有一个失败所有操作都失败。

事务场景引入

增加事务的步骤

在要增加事务的方法内添加如下的代码流程

1、开启事务

2、进行业务操作

3、如果没有发生异常,事务提交

4、如果在处理业务逻辑时出现异常,事务回滚

 @Autowired
    private UserDao userDao;
    public void transferAccount() {
        try{
        //1.开启事务
        //2.业务逻辑的处理
        //小明转账100,减少100元
        userDao.reduceMoney();
        //小红账户增加100元
        userDao.addMoney();
        }catch(Exception e){
        //3.如果存在异常,事务回滚
        }
        //4.事务提交
        
    }

思考一个问题,如果系统中存在很多功能,每个功能的业务逻辑处理的Service类都需要增加事务代码的编写,会发生什么问题?

第一,代码会变得非常臃肿,冗余。

第二,业务方法功能变得复杂而不清晰。

然而,Spring集成了对事务的管理,恰恰解决了这些问题,大大减少了程序员的代码量,也对事务有了很好的管理控制。业务层只需要专心业务的处理。

本章节主要对事务进行了一个介绍,如何用Spring实现事务管理我们将会下个章节介绍。

猜你喜欢

转载自blog.csdn.net/java_cxrs/article/details/108480570