再看Spring事务

1. 事务

数据库操作的最基本单元,逻辑上的一组操作,要么都成功,如果一个操作失败那么这组操作都会失败。

1.1 事务的四个特性(ACID)

  1. 原子性:操作不可分割,要么都成功,要不都失败;
  2. 一致性:操作前后的总量是不变的;
  3. 隔离性:同时操作同一条数据不会互相影响;
  4. 持久性:修改表中数据。

1.2 环境搭建

这里以银行转账为例子来展示Spring事务。
包含两个核心业务,即转款和收款。

  • Sql
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for t_user
-- ----------------------------
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user`  (
  `id` int(20) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `money` int(20) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of t_user
-- ----------------------------
INSERT INTO `t_user` VALUES (1, 'lucy', 200);
INSERT INTO `t_user` VALUES (2, 'mary', 200);

SET FOREIGN_KEY_CHECKS = 1;

  • maven
        <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>5.2.8.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.8.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>5.2.8.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-expression</artifactId>
            <version>5.2.8.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.8</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>5.2.8.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.7</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.8.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.38</version>
        </dependency>

    </dependencies>
  • 数据库配置文件
druid.driverClass = com.mysql.jdbc.Driver
druid.url = jdbc:mysql:///spring220529?useSSL=false
druid.username = root
druid.password = root
  • bean配置文件
<?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="druid.properties"/>

    <bean id="druid" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
        <property name="driverClassName" value="${druid.driverClass}"/>
        <property name="url" value="${druid.url}"/>
        <property name="username" value="${druid.username}"/>
        <property name="password" value="${druid.password}"/>
    </bean>

    <context:component-scan base-package="com.jm.spring.*"/>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="druid"/>
    </bean>
</beans>
  • Dao
/**
 * @Description
 * @date 2022/5/29 14:33
 */
public interface UserDao {

    /**
     * 多钱方法
     * @param id
     * @param money
     * @return
     */
    boolean addMoney(Integer id, Integer money);

    /**
     * 少钱方法
     * @param id
     * @param money
     * @return
     */
    boolean reduceMoney(Integer id, Integer money);

}

@Repository
public class UserDaoImpl implements UserDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public boolean addMoney(Integer id, Integer money) {
        String sql = "update t_user set money = money + ? where id = ?";
        return jdbcTemplate.update(sql, money, id) > 0;
    }

    @Override
    public boolean reduceMoney(Integer id, Integer money) {
        String sql = "update t_user set money = money - ? where id = ?";
        return jdbcTemplate.update(sql, money, id) > 0;
    }
}
  • Service
/**
 * @Description
 * @date 2022/5/29 14:34
 */
public interface UserService {

    /**
     * 转账方法
     * @param form 发起人
     * @param to 接收人
     * @param money 金额
     * @return
     */
    boolean accountMoney(Integer form, Integer to, int money);

}
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    @Override
    public boolean accountMoney(Integer form, Integer to, int money) {
        return userDao.reduceMoney(form,money) && userDao.addMoney(to,money);
    }
}

  • 测试类
public class Test {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("druid-bean.xml");
        UserServiceImpl userService = context.getBean("userServiceImpl", UserServiceImpl.class);
        userService.accountMoney(1,2,100);
    }
}

1.3 事务管理

  1. 一般在**业务逻辑层(服务层)**中添加事务。
  2. 分为编程式事务管理(基本不用)和声明式事务管理
  3. 声明式事务管理可以通过注解和xml配置文件来实现,底层也是使用AOP来实现。
  4. 通过顶层PlatformTransactionManager接口对不同的数据库框架有不同的是实现:在这里插入图片描述

1.4 声明式事务管理——注解

  • 配置文件中配置事务管理器
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--        指定数据源-->
        <property name="dataSource" ref="druid"/>
    </bean>
  • 配置文件中开启事务管理
    1. 修改头文件
    <?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/context/spring-tx.xsd">
    
    
    1. 开启事务注解
         <tx:annotation-driven transaction-manager="transactionManager"/>
    
    
    1. service添加事务注解,添加到类上当前类的所有方法都开启事务;如果添加到方法上,只是当前方法开启事务。
    @Transactional
    public class UserServiceImpl implements UserService {
    
    

这样添加之后,Spring会抛出一个新的错误:

Exception in thread "main" org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'userServiceImpl' is expected to be of type 'com.jm.spring.transaction.service.Impl.UserServiceImpl' but was actually of type 'com.sun.proxy.$Proxy26'

即类型强转错误,这里直接不实现接口即可解决问题,并且实现事务的回滚。

public class UserServiceImpl

1.5 事务参数

  • propagation,事务传播行为:多事务方法(增、删、改)互相调用,针对事务进行管理的过程。
    分为以下七种:
  • ioslation,事务隔离级别:事务有隔离性,多事务之间操作不会产生影响。
    不存在隔离性造成的三个问题:
    1. 脏读:一个未提交的事务读取到另一个未提交事务数据;
    2. 不可重复读:一个未提交的事务读取到另一个提交事务修改的数据;
    3. 幻读:一个未提交事务读取到另一个提交事务添加的数据。

设置隔离级别参数:
在这里插入图片描述

  • timeout,超时时间:如果在该时间内事务未提交成功,就进行回滚操作。默认**-1**,单位是秒。
  • readOnly,是否只读:默认false,即可以进行任何操作,如果设置true,就只能进行查询。
  • rollbackFor,回滚:设置出现某个异常进行回滚
  • noRollbackFor,不回滚:设置出现某个异常不进行回滚
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值