Spring 15: SM框架(Spring + MyBatis)事务处理

2年前 (2022) 程序员胖胖胖虎阿
362 0 0

实体类

  • Account实体类
package com.example.pojo;

public class Account {
    private Integer aid;
    private String aname;
    private String acontent;
    
    //getter,setter,无参构造,全参构造,toString方法不再赘述
}

数据访问层

  • AccountMapper接口
package com.example.mapper;

import com.example.pojo.Account;

/**
 * 数据库访问层接口
 */
public interface AccountMapper {
    int save(Account account);
}
  • AccountMapper.xml映射文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.AccountMapper">
    <!--
        int save(Account account);
        private Integer aid;
        private String aname;
        private String acontent
    -->
    <insert id="save" parameterType="account">
        insert into accounts values (#{aid}, #{aname}, #{acontent})
    </insert>

</mapper>

业务逻辑层

  • AccountService业务接口
package com.example.service;


import com.example.pojo.Account;

/**
 * 业务接口
 */
public interface AccountService {
    int save(Account account);
}
  • AccountServiceImpl业务实现类
package com.example.service.impl;

import com.example.mapper.AccountMapper;
import com.example.pojo.Account;
import com.example.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * 业务实现类
 */
@Service
public class AccountServiceImpl implements AccountService {
    //业务逻辑层应当持有数据访问层的接口类型的成员变量
    @Autowired
    AccountMapper accountMapper;

    @Override
    public int save(Account account) {
        int num = accountMapper.save(account);
        if(num == 1){
            System.out.println("账户创建成功!");
        }else{
            System.out.println("账户创建失败!");
        }
        return num;
    }
}

测试1

  • 当程序正常执行时
package com.example.test;

import com.example.pojo.Account;
import com.example.service.AccountService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestAccountSave {
    //测试账户创建
    @Test
    public void testAccountSave(){
        //创建Spring容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext_service.xml");
        //获取业务逻辑层对象
        AccountService accountService = (AccountService) ac.getBean("accountServiceImpl");
        //调用业务逻辑
        int num = accountService.save(new Account(20, "荷包蛋", "富婆的账户"));
        System.out.println("num: " + num);
    }

测试输出

  • 在控制台的输出中显示,账户信息成功导入
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@5dbe30be] was not registered for synchronization because synchronization is not active
Aug 28, 2022 3:42:05 PM com.alibaba.druid.support.logging.JakartaCommonsLoggingImpl info
INFO: {dataSource-1} inited
JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@62c5bbdc] will not be managed by Spring
==>  Preparing: insert into accounts values (?, ?, ?)
==> Parameters: 20(Integer), 荷包蛋(String), 富婆的账户(String)
<==    Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@5dbe30be]
账户创建成功!
num: 1

Process finished with exit code 0
  • 数据表中也成功导入账户信息

Spring 15: SM框架(Spring + MyBatis)事务处理

测试2

  • 在上述业务实现类中手动添加:1/0 的程序异常
@Service
public class AccountServiceImpl implements AccountService {
   
    @Autowired
    AccountMapper accountMapper;

    @Override
    public int save(Account account) {
        int num = accountMapper.save(account);
        if(num == 1){
            System.out.println("账户创建成功!");
        }else{
            System.out.println("账户创建失败!");
        }
        
        //手动添加异常
        System.out.println(1/0);
        
        return num;
    }
}

测试输出

  • 从控制台的输出可以看到,在出错前sql语句正常执行完毕,在出错后程序报错终止,此时不能确定新的账户信息有没有导入数据库

Spring 15: SM框架(Spring + MyBatis)事务处理

  • 从数据表中可以确定,此时的SM框架没有开启自动回滚机制,虽然程序出错,但是在程序出错前导入到数据表中的数据是有效的,这显然不符合业务需求

Spring 15: SM框架(Spring + MyBatis)事务处理

applicationContext_service.xml

  • 在业务逻辑层的配置文件applicationContext_service.xml中(也就是上一篇博文Spring 14: SM框架(Spring + MyBatis)初步整合中的该文件),添加sm框架的事务配置
    <!-- 添加事务处理 -->

    <!-- 1.添加事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 由于事务管理必然要涉及到数据库的操作,例如数据回滚等等,所以必须添加数据源配置 -->
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- 2.添加事务的注解驱动 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>

测试3

  • 在这里额外输出一下获取到的业务逻辑层对象的类型
    //测试账户创建
    @Test
    public void testAccountSave(){
        //创建Spring容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext_service.xml");
        //获取业务逻辑层对象
        AccountService accountService = (AccountService) ac.getBean("accountServiceImpl");
        System.out.println("获取到的业务逻辑层对象的类型: " + accountService.getClass());
        //调用业务逻辑
        int num = accountService.save(new Account(23, "荷包蛋4", "富婆的账户4"));
        System.out.println("num: " + num);
    }

测试输出

  • 从控制台的输出可以看出,此时获取到的业务逻辑层对象的类型:class com.example.service.impl.AccountServiceImpl,不是jdk动态代理的类型,说明事务切面并没有真正开启

Spring 15: SM框架(Spring + MyBatis)事务处理

  • 从数据表可以看出,虽然在applicationContext_service.xml中添加了事务机制,但是数据的回滚并没有成功

Spring 15: SM框架(Spring + MyBatis)事务处理

注意

  • 上面虽然在SM框架中添加了事务配置,但是在添加了事务的注解驱动后,并没有在业务逻辑层的业务实现类中使用该注解,导致事务切面并没有真正切入

测试4

  • 为业务实现类添加事务驱动的注解
@Service
@Transactional(propagation = Propagation.REQUIRED) //添加事务驱动的注解,该事务对增删改都适用
public class AccountServiceImpl implements AccountService {
	//......
}

测试输出

  • 从控制台的输出可以看出,此时获取到的业务逻辑层对象的类型:class com.sun.proxy.$Proxy21,是jdk动态代理的类型,说明事务切面真正导入

Spring 15: SM框架(Spring + MyBatis)事务处理

  • 从数据表可以看出数据的回滚也确实执行成功,记录"24 荷包蛋5 富婆的账户5"在程序出错后成功回滚,并没有真正导入到数据表中

Spring 15: SM框架(Spring + MyBatis)事务处理

设置不回滚属性

  • 上述事务的注解驱动还可以根据程序发生的异常名称,选择不回滚
@Transactional(propagation = Propagation.REQUIRED, noRollbackForClassName = {"异常名称1", "异常名称2", "异常名称3"})//多个异常名称
@Transactional(propagation = Propagation.REQUIRED, noRollbackForClassName = "异常名称1")//单个异常名称

测试5

  • 当设置:发生算术异常不回滚时,出现该种名称的错误时,已经导入到数据库中的数据,不会回滚
@Service
@Transactional(propagation = Propagation.REQUIRED, noRollbackForClassName = "ArithmeticException")
public class AccountServiceImpl implements AccountService {
    @Autowired
    AccountMapper accountMapper;

    @Override
    public int save(Account account) {
        int num = accountMapper.save(account);
        if(num == 1){
            System.out.println("账户创建成功!");
        }else{
            System.out.println("账户创建失败!");
        }
        //手动添加异常
        System.out.println(1/0);
        return num;
    }
}

测试输出

  • 从控制台可以看出,程序出错前sql语句执行成功,之后程序终止,也可以看到jdk动态代理成功执行,事务切面成功导入

Spring 15: SM框架(Spring + MyBatis)事务处理

  • 从数据表可以看到,该记录,成功导入数据表并有效,是因为我们回避了事务机制在遇到算术异常时的回滚机制,并未回滚已经导入的数据

Spring 15: SM框架(Spring + MyBatis)事务处理

版权声明:程序员胖胖胖虎阿 发表于 2022年10月25日 上午9:40。
转载请注明:Spring 15: SM框架(Spring + MyBatis)事务处理 | 胖虎的工具箱-编程导航

相关文章

暂无评论

暂无评论...