事务

1、事务回顾

1 事务的概念
事务(Transaction)指的是一个操作序列,该操作序列中的多个操作要么都做,要么都不做,是一个不可分割的工作单位,是数据库环境中的逻辑工作单位,由DBMS中的事务管理子系统负责事务的处理。

目前常用的存储引擎有InnoDB(MySQL5.5以后默认的存储引擎)和MyISAM(MySQL5.5之前默认的存储引擎),其中InnoDB支持事务处理机制,而MyISAM不支持。

2 事务的特性

事务处理可以确保除非事务性序列内的所有操作都成功完成,否则不会永久更新面向数据的资源。通过将一组相关操作组合为一个要么全部成功要么全部失败的序列,可以简化错误恢复并使应用程序更加可靠。

但并不是所有的操作序列都可以称为事务,这是因为一个操作序列要成为事务,必须满足事务的原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。这四个特性简称为ACID特性。
1) 原子性
原子是自然界最小的颗粒,具有不可再分的特性。事务中的所有操作可以看做一个原子,事务是应用中不可再分的最小的逻辑执行体。

使用事务对数据进行修改的操作序列,要么全部执行,要么全不执行。通常,某个事务中的操作都具有共同的目标,并且是相互依赖的。如果数据库系统只执行这些操作中的一部分,则可能会破坏事务的总体目标,而原子性消除了系统只处理部分操作的可能性。

2) 一致性
一致性是指事务执行的结果必须使数据库从一个一致性状态,变到另一个一致性状态。当数据库中只包含事务成功提交的结果时,数据库处于一致性状态。一致性是通过原子性来保证的。

例如:在转账时,只有保证转出和转入的金额一致才能构成事务。也就是说事务发生前和发生后,数据的总额依然匹配。

3) 隔离性

隔离性是指各个事务的执行互不干扰,任意一个事务的内部操作对其他并发的事务,都是隔离的。也就是说:并发执行的事务之间既不能看到对方的中间状态,也不能相互影响。

例如:在转账时,只有当A账户中的转出和B账户中转入操作都执行成功后才能看到A账户中的金额减少以及B账户中的金额增多。并且其他的事务对于转账操作的事务是不能产生任何影响的。

4) 持久性

持久性指事务一旦提交,对数据所做的任何改变,都要记录到永久存储器中,通常是保存进物理数据库,即使数据库出现故障,提交的数据也应该能够恢复。但如果是由于外部原因导致的数据库故障,如硬盘被损坏,那么之前提交的数据则有可能会丢失。

3 事务的并发问题
脏读(Dirty read)

当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的。

spring事务mysql实现原理 spring和mysql事务有什么区别_spring


不可重复读(Unrepeatableread): 指在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。

spring事务mysql实现原理 spring和mysql事务有什么区别_bc_02


幻读(Phantom read): 幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。

spring事务mysql实现原理 spring和mysql事务有什么区别_spring事务mysql实现原理_03


不可重复度和幻读区别:

不可重复读的重点是修改,幻读的重点在于新增或者删除。
解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表

例1(同样的条件, 你读取过的数据, 再次读取出来发现值不一样了 ):事务1中的A先生读取自己的工资为 1000的操作还没完成,事务2中的B先生就修改了A的工资为2000,导 致A再读自己的工资时工资变为 2000;这就是不可重复读。

例2(同样的条件, 第1次和第2次读出来的记录数不一样 ):假某工资单表中工资大于3000的有4人,事务1读取了所有工资大于3000的人,共查到4条记录,这时事务2又插入了一条工资大于3000的记录,事务1再次读取时查到的记录就变为了5条,这样就导致了幻读

4 事务的隔离级别

事务的隔离级别用于决定如何控制并发用户读写数据的操作。数据库是允许多用户并发访问的,如果多个用户同时开启事务并对同一数据进行读写操作的话,有可能会出现脏读、不可重复读和幻读问题,所以MySQL中提供了四种隔离级别来解决上述问题。

事务的隔离级别从低到高依次为READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ以及SERIALIZABLE,隔离级别越低,越能支持高并发的数据库操作。

spring事务mysql实现原理 spring和mysql事务有什么区别_bc_04


spring中可以使用如下方式实现事务的控制

1 编程式(不推荐)

2 声明式(掌握)
1) 注解(简单,必会)

2)XML配置(繁琐,了解)

2、Spring_事务环境搭建

通过张三给李四转账案例演示事务的控制
数据库中准备表格

create table account1(
		id int primary key auto_increment,
        name varchar(10) not null,
        money double     
);
-- 查看表中数据:
select * from account1;
insert into account1 values(1,'小明',2000),(2,'小花',2000);

spring事务mysql实现原理 spring和mysql事务有什么区别_bc_05


项目结构

spring事务mysql实现原理 spring和mysql事务有什么区别_数据_06

导入依赖

<packaging>jar</packaging>
    <dependencies>
        <!--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>
        <!--Junit单元测试-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.1</version>
            <scope>test</scope>
        </dependency>
        <!--lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

准备JDBC.properties

jdbc_username=root
jdbc_password=root
jdbc_driver=com.mysql.cj.jdbc.Driver
jdbc_url=jdbc:mysql://127.0.0.1:3306/four tables?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true

准备applicationcontext.xml

xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd

<!--spring 注解扫描-->
    <context:component-scan base-package="com.wml"/>
    <!--读取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>

项目中准备实体类

@AllArgsConstructor
@NoArgsConstructor
@Data
public class Account1 {
    private Integer id;
    private String name;
    private Integer money;
}

准备DAO层,创建一个根据id修改money的方法

public interface Account1Dao {
    int transMoney(int id,int money);
}
@Repository
public class Account1DaoImpl implements Account1Dao {
    @Autowired
    private JdbcTemplate jdbcTempLate;
    @Override
    public int transMoney(int id, int money) {
        String sql = "update account1 set money = money +? where id =?";
        return jdbcTempLate.update(sql,money,id);
    }
}

== 准备Service,创建一个转账的业务方法==

public interface Account1Service {
    int transMoney(int from,int to,int money);
}
@Service
public class Account1ServiceImpl implements Account1Service {
    @Autowired
    private Account1Dao account1Dao;
    @Override
    public int transMoney(int from, int to, int money) {
        int rows = 0;
        /*转出操作*/
        rows += account1Dao.transMoney(from,0-money);
		/*制造异常*/
        int i = 1/0;
		/*转入操作*/
        rows += account1Dao.transMoney(to,money);
        return rows;
    }
}

测试代码,测试转账

public class Test01 {
    @Test
    public void testTranMoney(){
        ApplicationContext context =new ClassPathXmlApplicationContext("applicationcontext.xml");
        Account1Service account1Service = context.getBean(Account1Service.class);
        int rows = account1Service.transMoney(1, 2, 200);
        System.out.println(rows);
    }
}

3、Spring_事务管理注解方式

事务的管理应该放在我们的service层进行处理
spring中有两种事务的管理方式
1 编程式事务管理(了解)
2 声明式事务管理(掌握)
基于注解方式实现(掌握)
XML方式实现(了解)
Spring声明式事务的实现方式,底层就是AOP,AOP的底层就是动态代理
Spring事务管理相关的API

事务管理器接口: PlatformTransactionManager 针对不同的框架,提供了不同的实现类

spring事务mysql实现原理 spring和mysql事务有什么区别_spring_07


注解方式实现事务控制

在applicationContext.xml配置事务相关的配置

xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
       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 注解扫描-->
    <context:component-scan base-package="com.wml"/>
    <!--读取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>
    <!--配置德鲁伊连接池-->
    <bean id="dataSource2" 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>
    <!--声明一个事务管理器-->
    <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--注入数据源-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--开启事务的注解-->
    <tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>

在Service层中添加事务的注解
@Transactional
放在方法上,就是仅仅对当前方法增加了事务控制
放在方法上,就是仅仅对当前方法增加了事务控制

@Service
//@Transactional  //加在类上,代表类中的所有方法都添加了事务控制
public class Account1ServiceImpl implements Account1Service {
    @Autowired
    private Account1Dao account1Dao;
    @Override
    @Transactional  // 放在方法上,就是仅仅对当前方法增加了事务控制
    public int transMoney(int from, int to, int money) {
        int rows = 0;
        /*转出操作*/
        rows += account1Dao.transMoney(from,0-money);
        /*转入操作*/
        int i = 1/0;
        rows += account1Dao.transMoney(to,money);
        return rows;
    }
}

再次测试,就算是service方法运行出现异常,自动会回滚,如果没有,那么自动提交

@Transactional 注解的一些参数和参数的含义
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT,readOnly = false,rollbackFor = NullPointerException.class,norollbackFor = ClassCastException.class,timeout = 10)

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
    @AliasFor("transactionManager")
    String value() default "";
    @AliasFor("value")
    String transactionManager() default "";
    String[] label() default {};
    Propagation propagation() default Propagation.REQUIRED;
    Isolation isolation() default Isolation.DEFAULT;
    int timeout() default -1;
    String timeoutString() default "";
    boolean readOnly() default false;
    Class<? extends Throwable>[] rollbackFor() default {};
    String[] rollbackForClassName() default {};
    Class<? extends Throwable>[] noRollbackFor() default {};
    String[] noRollbackForClassName() default {};
}

propagation 事务的传播行为(面试)
多事务方法之间调用,事务是如何管理的

事务传播行为类型

说明

PROPAGATION_REQUIRED

如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择(默认)。

PROPAGATION_SUPPORTS

支持当前事务,如果当前没有事务,就以非事务方式执行。

PROPAGATION_MANDATORY

使用当前的事务,如果当前没有事务,就抛出异常。

PROPAGATION_REQUIRES_NEW

新建事务,如果当前存在事务,把当前事务挂起。

PROPAGATION_NOT_SUPPORTED

以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

PROPAGATION_NEVER

以非事务方式执行,如果当前存在事务,则抛出异常。

PROPAGATION_NESTED

如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

如果service层 add方法调用了 addDept和addEmp两个方法

PROPAGATION_REQUIRED
如果add方法有事务,那么addDept和addEmp就加入到add方法里的事务
如果add方法没有事务,那么就新建一个事务,将addDept和addEmp加入到这个新的事务中

PROPAGATION_REQUIRES_NEW
无论add是否有事务,都建立一个新的事务,所有的方法都加入到新的事务中,add原来的事务就不用了

isolation 事务的隔离级别

  1. DEFAULT (默认)
    这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别。另外四个与JDBC的隔离级别相对应。
    MySQL默认REPEATABLE_READ
    Oracle默认READ_COMMITTED
  2. READ_UNCOMMITTED (读未提交)
    这是事务最低的隔离级别,它允许另外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读。
  3. READ_COMMITTED (读已提交)
    保证一个事务修改的数据提交后才能被另外一个事务读取,另外一个事务不能读取该事务未提交的数据。这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻像读。
  4. REPEATABLE_READ (可重复读)
    这种事务隔离级别可以防止脏读、不可重复读,但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了不可重复读。
  5. SERIALIZABLE(串行化)
    这是花费最高代价但是最可靠的事务隔离级别,事务被处理为顺序执行。除了防止脏读、不可重复读外,还避免了幻像读。

timeout 超时时间
事务一定要在多长时间之内提交,如果不提交就会回滚

readOnly 只读事务
事务是否只能读取数据库的数据,如果为true,则不允许进行增删改

rollbackFor 指定发生回滚的异常
当方法发生哪些异常时才会回滚

noRollbackFor 指定不发生回滚的异常
当方法发生哪些异常时,不会回滚

4、Spring_事务管理XML配置方式

applicationContext中,通过AOP实现事务的控制

<!--spring 注解扫描-->
    <context:component-scan base-package="com.wml"/>
    <!--读取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>
    <!--配置德鲁伊连接池-->
    <bean id="dataSource2" 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>
    <!--声明一个事务管理器-->
    <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--注入数据源-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--配置通知-->
    <tx:advice id="transactionInterceptor">
        <tx:attributes><!--属性-->
            <tx:method name="transMoney" isolation="DEFAULT" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>
    <!--配置Aop-->
    <aop:config>
        <!--配置切入点-->
        <aop:pointcut id="pt" expression="execution(* com.wml.Service.Account1Service.transMoney(..))"/>
        <!--配置切面-->
        <aop:advisor advice-ref="transactionInterceptor" pointcut-ref="pt"></aop:advisor><!--通知和切入点构成切面-->
    </aop:config>

导入依赖(Spring_事务管理注解方式一样)
准备JDBC.properties、项目中准备实体类、准备DAO层,创建方法准备Service,创建方法(Spring_事务管理注解方式一样)、测试代码和Spring_事务环境搭建一样

5、Spring_零XML事务控制

项目结构

spring事务mysql实现原理 spring和mysql事务有什么区别_bc_08


创建配置类

@Configurable // 配置类标志注解
@ComponentScan("com.wml") // spring包扫描
@PropertySource("classpath:jdbc.properties") // 读取属性配置文件
@EnableTransactionManagement   // 开启事务注解
public class SpringConfig {
    @Value("${jdbc_username}")
    private String username;
    @Value("${jdbc_password}")
    private String password;
    @Value("${jdbc_url}")
    private String url;
    @Value("${jdbc_driver}")
    private String driver;
    /*相当于applicationcontext.xml中德鲁伊数据源*/
    @Bean
    public DruidDataSource getDruidDataSource(){
        DruidDataSource DataSource = new DruidDataSource();
        DataSource.setPassword(password);
        DataSource.setUsername(username);
        DataSource.setDriverClassName(driver);
        DataSource.setUrl(url);
        return DataSource;
    }
    /*相当于applicationcontext.xml配置JDBCTemplate对象,并向里面注入数据源(DataSource)*/
    @Bean
        public JdbcTemplate jdbcTemplate(DataSource dataSource){
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        /*注入数据源*/
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }
    /*声明一个事务管理器,并注入数据源(DataSource)*/
    @Bean
    public PlatformTransactionManager getPlatformTransactionManager(DataSource dataSource){
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        /*注入数据源*/
        dataSourceTransactionManager.setDataSource(dataSource);
        return dataSourceTransactionManager;
    }
}

测试代码

public class Test03 {
    @Test
    public void testTranMoney(){
        ApplicationContext context =new AnnotationConfigApplicationContext(SpringConfig.class);
        Account1Service account1Service = context.getBean(Account1Service.class);
        int rows = account1Service.transMoney(1, 2, 200);
        System.out.println(rows);
    }
}

导入依赖(Spring_事务管理注解方式一样)
准备JDBC.properties、项目中准备实体类、准备DAO层,创建方法准备Service,创建方法(Spring_事务管理注解方式一样)、和Spring_事务环境搭建一样

6、Spring_日志框架和测试支持

spring5框架自带了通用的日志封装,也可以整合自己的日志
1)spring移除了 LOG4jConfigListener,官方建议使用log4j2
2)spring5整合log4j2
导入log4j2依赖

<!--log4j2 依赖-->
        <!--<dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.14.0</version>
        </dependency>-->
        <!--slf4-impl 包含了log4j2 依赖-->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
            <version>2.14.0</version>
            <scope>test</scope>
        </dependency>

在resources目录下准备log4j2.xml的配置文件

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="DEBUG">
    <!--日志的输出方式-->
    <Appenders>
        <!--往控制台上输出-->
        <Console name="Console" target="SYSTEM_ERR">
            <PatternLayout pattern="%d{YYYY-MM-dd HH:mm:ss} [%t] %-5p %c{1}:%L - %msg%n" />
        </Console>
        <!--往文件上输出-->
        <RollingFile name="RollingFile" filename="log/test.log"
                     filepattern="${logPath}/%d{YYYYMMddHHmmss}-fargo.log">
            <PatternLayout pattern="%d{YYYY-MM-dd HH:mm:ss} [%t] %-5p %c{1}:%L - %msg%n" />
            <Policies>
                <SizeBasedTriggeringPolicy size="10 MB" />
            </Policies>
            <DefaultRolloverStrategy max="20" />
        </RollingFile>
    </Appenders>
    <Loggers>
        <Root level="INFO">
            <AppenderRef ref="Console" />
        </Root>
    </Loggers>
</Configuration>

spring5关于测试工具的支持
整合junit4
依赖的jar

<!--Junit单元测试-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.1</version>
            <scope>test</scope>
        </dependency>
        <!--spring test测试支持包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.3.5</version>
            <scope>test</scope>
        </dependency>

测试代码编写方式

@RunWith(SpringJUnit4ClassRunner.class) // 指定测试支持类
@ContextConfiguration("classpath:applicationcontext.xml") // 指定核心配置文件位置
public class Test04 {
    @Autowired // 注入要获取的bean
    private Account1Service account1Service;
    @Test
    public void testTranMoney(){
        int rows = account1Service.transMoney(1, 2, 200);
        System.out.println(rows);
    }
}

整合junit5
依赖的jar

<!--junit5单元测试-->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.7.0</version>
            <scope>test</scope>
        </dependency>

测试代码编写方式

/*@ExtendWith(SpringExtension.class) // 指定测试支持类
@ContextConfiguration("classpath:applicationcontext.xml") // 指定核心配置文件位置*/
// 使用复合注解
@SpringJUnitConfig(locations="classpath:applicationcontext.xml")
public class Test05 {
    @Autowired // 注入要获取的bean
    private Account1Service account1Service;
    @Test
    public void testTranMoney(){
        int rows = account1Service.transMoney(1, 2, 200);
        System.out.println(rows);
    }
}