Spring-4-Spring中的事务和2种事务管理方式的代码演示(Spring自带的事务管理器和AspectJ事务管理器)

目录

1、关于事务, 先回答一些问题(重要)

2、Spring 的事务管理

2.1 Spring 事务管理 API

2.1.1 (1)事务管理器接口(重点)

【A】常用的两个实现类

【B】Spring 的回滚方式(理解)

【C】回顾错误与异常(理解)

2.1.1 (2)事务定义接口

【A】定义了五个事务隔离级别常量(掌握)

【B】定义了七个事务传播行为常量(掌握)

【C】定义了默认事务超时时限

3、Spring中事务环境搭建(使用Spring自带的事务管理机制做代码演示)

3.1 创建商品表RUN_Goods和销售表RUN_Sale

3.2 添加依赖

3.2.1 添加依赖:pom.xml

3.2.2 创建SSM的web项目需要的全局配置文件:web.xml

3.3 创建SpringMVC用的配置文件:springmvc.xml

3.4 创建MyBatis用的配置文件:mybatis.xml

3.4.1 编写数据库的连接配置文件:jdbc.properties

3.5 创建Spring用的配置文件(包含事务管理器配置):spring.xml

3.6 编写dao层接口:GoodsDao.java + SaleDao.java

3.7 编写dao层级对应的mapper文件:GoodsDao.xml + SaleDao.xml

3.8 编写Service层接口:buyGoodsService.java

3.9 编写Service层接口的实现类(使用Spring事务管理的注解):buyGoodsServiceImpl.java

3.10 编写一个运行时异常类:NotEnoughException.java

3.11 编写测试基类:BaseTest.java

3.12 编写测试类:buyServiceTest.java

3.12.1 程序正常运行不抛出异常时的测试结果

3.12.2 程序抛出“商品不存在”的运行时异常的测试结果

3.12.3 程序抛出“库存不足”的运行时异常的测试结果

3.12.4 Spring中的事务管理器的确发生作用了, 看表中数据的主键ID

3.12.5 附录2个实体类:SaleEntity和GoodsEntity

3.12.6 附录项目结构图

3.12.7 使用 Spring 的事务注解管理事务(掌握@Transactional )

4、Spring中事务环境搭建(使用AspectJ的事务管理机制做代码演示)

4.1 在pom.xml文件中引入AspectJ管理事务的依赖:只需关注“第二种”即可

4.2 在spring.xml文件中配置AspectJ事务管理器和AOP切入点表达式

5、完结撒花-项目结构

参考B站动力节点视频https://www.bilibili.com/video/BV1nz4y1d7uy?p=96

1、关于事务, 先回答一些问题(重要)

Spring处理事务的流程:

2、Spring 的事务管理

事务,原本是数据库中的概念,在 Dao 层。但一般情况下,需要将事务提升到业务层,即 Service 层。这样做是为了能够使用事务的特性来管理具体的业务。

在 Spring 中通常可以通过以下两种方式来实现对事务的管理:(1)使用 Spring 的事务注解管理事务。(2)使用 AspectJ 的 AOP 配置管理事务。

2.1 Spring 事务管理 API

Spring 的事务管理,主要用到两个事务相关的接口。

2.1.1 (1)事务管理器接口(重点)

事务管理器是 PlatformTransactionManager 接口对象。其主要用于完成事务的提交、回滚,及获取事务的状态信息。

【A】常用的两个实现类

PlatformTransactionManager 接口有两个常用的实现类:
(1)DataSourceTransactionManager:使用 JDBC 或 MyBatis 进行数据库操作时使用。

(2)HibernateTransactionManager:使用 Hibernate 进行持久化数据时使用。

【B】Spring 的回滚方式(理解)

Spring 事务的默认回滚方式是:发生运行时异常和 error 时回滚,发生受查(编译时异常)异常时提交。不过,对于受查异常,程序员也可以手工设置其回滚方 式。

【C】回顾错误与异常(理解)

Throwable 类是 Java 语言中所有错误或异常的超类。只有当对象是此类 (或其子类之一)的实例时,才能通过 Java 虚拟机或者 Java 的 throw 语句抛出。

Error 是程序在运行过程中出现的无法处理的错误,比如 OutOfMemoryError、NoSuchMethodError 等。当这些错误发生时,程序是无法处理(捕获或抛出)的,JVM 一般会终止线程。

程序在编译和运行时出现的另一类错误,称之为异常,它是 JVM 通知程序员的一种方式。通过这种方式,让程序员知道已经或可能出现错误,要求程序员对其进行处理。异常分为运行时异常与受查异常。

(1)运行时异常,是 RuntimeException 类或其子类,即只有在运行时才出现的异常。如,NullPointerException、ArrayIndexOutOfBoundsException、 IllegalArgumentException 等均属于运行时异常。这些异常由 JVM 抛出,在编译时不要求必须处理(捕获或抛出)。但,只要代码编写足够仔细,程序足够健壮,运行时异常是可以避免的。

(2)受查异常,也叫编译时异常,即在代码编写时要求必须捕获或抛出的异常,若不处理,则无法通过编译。如 SQLException, ClassNotFoundException,IOException 等都属于受查异常。

注意:RuntimeException 及其子类以外的异常,均属于受查异常。当然,用户自定义的 Exception 的子类,即用户自定义的异常也属受查异常。程序员在定义异常时,只要未明确声明定义的为 RuntimeException 的子类,那么定义的就是受查异常。

2.1.1 (2)事务定义接口

事务定义接口 TransactionDefinition 中定义了事务描述相关的三类常量:事务隔离级别、事务传播行为、事务默认超时时限,及对它们的操作。

【A】定义了五个事务隔离级别常量(掌握)

这些常量均是以 ISOLATION_开头,即形如 ISOLATION_XXX:
(1)DEFAULT:采用 DB 默认的事务隔离级别。MySql 的默认为 REPEATABLE_READ。Oracle 默认为 READ_COMMITTED。

(2)READ_UNCOMMITTED:读未提交。未解决任何并发问题。

(3) READ_COMMITTED:读已提交。解决脏读,存在不可重复读与幻读。

(4)REPEATABLE_READ:可重复读。解决脏读、不可重复读,存在幻读。

(5)SERIALIZABLE:串行化。不存在并发问题。

【B】定义了七个事务传播行为常量(掌握)

所谓事务传播行为是指,处于不同事务中的方法在相互调用时,执行期间事务的维护情况。如,A 事务中的方法 doSome() 调用 B 事务中的方法 doOther(),在调用执行期间事务的维护情况,就称为事务传播行为。事务传 播行为是加在方法上的。

事务传播行为常量都是以 PROPAGATION_ 开头,形如 PROPAGATION_XXX。

(1)PROPAGATION_REQUIRED

(2)PROPAGATION_REQUIRES_NEW

(3)PROPAGATION_SUPPORTS

(4)PROPAGATION_MANDATORY

(5)PROPAGATION_NESTED

(6)PROPAGATION_NEVER

(7)PROPAGATION_NOT_SUPPORTED

a、 PROPAGATION_REQUIRED:指定的方法必须在事务内执行。若当前存在事务,就加入到当前事务中; 若当前没有事务,则创建一个新事务。这种传播行为是最常见的选择,也是 Spring 默认的事务传播行为。如该传播行为加在 doOther()方法上。若 doSome()方法在调用 doOther() 方法时就是在事务内运行的,则 doOther()方法的执行也加入到该事务内执行。若 doSome()方法在调用 doOther()方法时没有在事务内执行,则 doOther()方法会创建一个事务,并在其中执行。

b、 PROPAGATION_SUPPORTS:指定的方法支持当前事务,但若当前没有事务,也可以以非事务方式执行。

c、 PROPAGATION_REQUIRES_NEW:总是新建一个事务,若当前存在事务,就将当前事务挂起,直到新事务执 行完毕。

【C】定义了默认事务超时时限

常量 TIMEOUT_DEFAULT 定义了事务底层默认的超时时限,sql 语句的执行时长。注意,事务的超时时限起作用的条件比较多,且超时的时间计算点较复杂。所以,该值一般就使用默认值即可。

3、Spring中事务环境搭建(使用Spring自带的事务管理机制做代码演示)

3.1 创建商品表RUN_Goods和销售表RUN_Sale

CREATE TABLE `RUN_Goods` (
  `Id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID(商品编号ID)',
  `Name` varchar(256) NOT NULL COMMENT '商品名称',
  `Account` int(11) NOT NULL DEFAULT '0' COMMENT '商品数量',
  `Price` int(11) NOT NULL DEFAULT '0' COMMENT '商品价格',
  `AddTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '添加时间',
  `UpdateTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`Id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='商品表';

CREATE TABLE `RUN_Sale` (
  `Id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID(销售记录ID)',
  `Gid` int(11) NOT NULL DEFAULT '0' COMMENT '商品编号ID',
  `Nums` int(11) NOT NULL DEFAULT '0' COMMENT '商品数量',
  `AddTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '添加时间',
  `UpdateTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`Id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='销售表';

3.2 添加依赖

3.2.1 添加依赖:pom.xml

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.wind</groupId>
    <artifactId>ssm-web-tx2-aspectj</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <name>ssm-web Maven Webapp</name>
    <!-- FIXME change it to the project's website -->
    <url>http://www.example.com</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <spring.version>5.3.2</spring.version>
        <mybatis.version>3.4.6</mybatis.version>
        <mybatis.spring.version>2.0.3</mybatis.spring.version>
        <mysql.version>8.0.22</mysql.version>
        <druid.version>1.2.4</druid.version>
    </properties>

    <dependencies>
        
        <!--使用Spring管理事务需要的依赖如下,2选1即可。
        (1)第一种:使用Spring自带的事务管理器需要的依赖。
        (2)第二种:使用AspectJ管理事务需要的依赖。
        -->

        <!--第二种:使用AspectJ管理事务需要的依赖=begin-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.1.4.RELEASE</version>
        </dependency>
        <!--第二种:使用AspectJ管理事务需要的依赖=end-->


        <!--第一种:使用Spring自带的事务管理器需要的依赖=begin-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <!--第一种:使用Spring自带的事务管理器需要的依赖=end-->


        <!--SpringMVC依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <!--SpringMVC使用的Servlet依赖-->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
        </dependency>

        <!--SpringMVC使用的jsp依赖-->
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.2.1-b03</version>
        </dependency>

        <dependency>
            <groupId>jstl</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>

        <!--MyBatis依赖-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>${mybatis.version}</version>
        </dependency>

        <!--MyBatis与SpringMVC整合时需要的依赖-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>${mybatis.spring.version}</version>
        </dependency>

        <!--mysql驱动依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
        </dependency>

        <!--mysql数据库连接池依赖:使用的是德鲁伊数据库连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>${druid.version}</version>
        </dependency>

        <!--springMVC序列化用的jackson依赖-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.10.2</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.10.2</version>
        </dependency>

        <!--单元测试依赖-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.2.5.RELEASE</version>
            <scope>test</scope>
        </dependency>

    </dependencies>


    <build>
        <finalName>ssm-web</finalName>
        <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
            <plugins>
                <plugin>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.1</version>
                    <configuration>
                        <source>${maven.compiler.source}</source>
                        <target>${maven.compiler.target}</target>
                    </configuration>
                </plugin>
                <plugin>
                    <artifactId>maven-clean-plugin</artifactId>
                    <version>3.1.0</version>
                </plugin>
                <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
                <plugin>
                    <artifactId>maven-resources-plugin</artifactId>
                    <version>3.0.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.8.0</version>
                </plugin>
                <plugin>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <version>2.22.1</version>
                </plugin>
                <plugin>
                    <artifactId>maven-war-plugin</artifactId>
                    <version>3.2.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-install-plugin</artifactId>
                    <version>2.5.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-deploy-plugin</artifactId>
                    <version>2.8.2</version>
                </plugin>
            </plugins>
        </pluginManagement>

        <!--
        maven默认扫描src/main/java中的文件而不理会src/main/resources中的xml文件,
        所以后来添加了resource节点,这样就将src/main/resources中的xml文件改变maven默认的扫描策略,
        防止造成src/main/resources下的配置文件打包丢失。
        编译之后的文件中少了mapper.xml,这个和maven有关,maven编译src/java代码的时候,
        默认只会对java文件进行编译然后放在target/classes目录,需要在pom.xml中加入下面配置-->
        <!--如果不添加此节点,mapper.xml文件、config.properties文件、config.spring文件等
        都不会被加载到target的classes中去,也就不能被使用,也就会报错。-->
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
            </resource>
        </resources>
    </build>

</project>

3.2.2 创建SSM的web项目需要的全局配置文件:web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!--注册前端控制器,也即整个web项目中唯一的一个后端中央控制器,用于分发给不同的Controller处理器-->
    <servlet>
        <servlet-name>springmvc-servlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:config/spring-mvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>springmvc-servlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!--注册Spring的监听器-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:config/spring.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!--注册字符集过滤器,一般使用Spring框架自带的字符集过滤器即可-->
    <filter>
        <filter-name>characterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceRequestEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>forceResponseEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>characterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!--加载静态资源-->
    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>*.js</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>*.css</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>*.jpg</url-pattern>
    </servlet-mapping>

</web-app>

3.3 创建SpringMVC用的配置文件:springmvc.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"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       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/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!--SpringMVC的配置文件,用来声名Controller和其他web相关的对象-->

    <!--配置组件扫描器-->
    <context:component-scan base-package="com.wind.controller"/>

    <!--视图解析器:添加前缀和后缀。
    SpringMVC框架为了避免对于请求资源路径与扩展名上的冗余,在视图解析器 InternalResouceViewResolver
    中引入了请求的前辍与后辍。而 ModelAndView 中只需给出要跳转页面的文件名即可,对于具体的文件路径与文件扩展名,
    视图解析器会自动完成拼接。-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--视图文件的路径-->
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <!--视图文件的扩展名-->
        <property name="suffix" value=".jsp"/>
    </bean>

    <!--注册注解驱动。
    (1)响应ajax请求,返回json字符串。
    (2)解决静态资源访问问题。-->
    <mvc:annotation-driven/>

    <!--加载静态资源图片啊,jQuery文件啊等等-->
    <mvc:resources location="js/" mapping="/js/**"/>
    <mvc:resources location="images/" mapping="/images/**"/>

</beans>

3.4 创建MyBatis用的配置文件:mybatis.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <!--<settings>-->
    <!--&lt;!&ndash; 打印SQL&ndash;&gt;-->
    <!--<setting name="logImpl" value="STDOUT_LOGGING"/>-->
    <!--</settings>-->

    <!--给实体类设置别名-->
    <typeAliases>
        <package name="com.wind.entity"/>
    </typeAliases>

    <!--SQL Mapper映射文件的位置-->
    <mappers>
        <!--name:是包名,这个包下的mapper文件能够一次性加载-->
        <!--package:使用这个属性的前提是:
        (1)mapper文件名称和dao接口名必须完全一样,包括大小写。
        (2)mapper文件和dao接口必须在同一目录下。-->
        <package name="com.wind.dao"/>
    </mappers>
    
</configuration>

3.4.1 编写数据库的连接配置文件:jdbc.properties

##数据库驱动
jdbc.driverClassName=com.mysql.cj.jdbc.Driver
##MySQL连接信息
jdbc.url=jdbc:mysql://127.0.0.1:3306/RUNOOB?useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=GMT
##用户名
jdbc.username=root
##密码
jdbc.password=root

3.5 创建Spring用的配置文件(包含事务管理器配置):spring.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"
       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的配置文件,用来声名service、dao、工具类等对象-->

    <!--加载连接mysql时需要的配置文件-->
    <context:property-placeholder location="classpath:config/jdbc.properties"/>

    <!--声名数据源,连接数据库-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
        <!--配置数据库连接池的初始化大小、最小、最大-->
        <property name="initialSize" value="5"/>
        <property name="minIdle" value="5"/>
        <property name="maxActive" value="20"/>
        <!--配置获取连接等待超时的时间,单位是毫秒-->
        <property name="maxWait" value="10000"/>
        <!--配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒-->
        <property name="timeBetweenEvictionRunsMillis" value="60000"/>
        <!--配置一个连接在池中最小生存的时间,单位是毫秒-->
        <property name="minEvictableIdleTimeMillis" value="300000"/>
    </bean>

    <!--声名一个SqlSessionFactoryBean,用它来创建SqlSessionFactory-->
    <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="configLocation" value="classpath:config/mybatis.xml"/>
    </bean>

    <!--声名MyBatis的扫描器,创建dao接口接口对象-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"/>
        <property name="basePackage" value="com.wind.dao"/>
    </bean>

    <!--声名service的注解@Service所在的包名-->
    <!--<context:component-scan base-package="com.wind.service,com.wind.serviceImpl"/>-->
    <context:component-scan base-package="com.wind.service*"/>

    <!--事务的配置:使用Spring自带的事务管理器来管理事务-->
    <!--1.声名一个事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--给事务管理器配置一个数据源,告诉Spring你需要管理的是这个数据库-->
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--2.开启事务注解驱动:告诉Spring使用注解管理事务,创建代理对象。
    transaction-manager:事务管理器对象的ID。-->
    <tx:annotation-driven transaction-manager="transactionManager"/>

</beans>

3.6 编写dao层接口:GoodsDao.java + SaleDao.java

package com.wind.dao;
import com.wind.entity.GoodsEntity;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;

@Repository
public interface GoodsDao {

    /**
     * 更新库存。goodsEntity 表示本次用户购买的商品信息
     */
    int updateGoods(@Param("goodsEntity") GoodsEntity goodsEntity);

    /**
     * 根据商品ID查询商品信息
     */
    GoodsEntity queryGoods(@Param("id") Integer id);
}


package com.wind.dao;
import com.wind.entity.SaleEntity;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;

@Repository
public interface SaleDao {

    //增加销售记录
    int insertSale(@Param("saleEntity") SaleEntity saleEntity);

}

3.7 编写dao层级对应的mapper文件:GoodsDao.xml + SaleDao.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.wind.dao.GoodsDao">

    <sql id="sql_select">
        select Id, Name, Account, Price, AddTime, UpdateTime from RUN_Goods
    </sql>

    <update id="updateGoods" parameterType="goodsEntity" useGeneratedKeys="true">
    update RUN_Goods set
        account = account - #{goodsEntity.account}
        where id = #{goodsEntity.id}
    </update>

    <select id="queryGoods" resultType="com.wind.entity.GoodsEntity">
        <include refid="sql_select"/>
        where id = #{id}
    </select>

</mapper>


<?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.wind.dao.SaleDao">

    <sql id="sql_select">
        select Id, Gid, Nums, AddTime, UpdateTime from RUN_Sale
    </sql>

    <insert id="insertSale" parameterType="saleEntity" keyProperty="saleEntity.id" useGeneratedKeys="true">
        insert into RUN_Sale
        (
          Gid,
          Nums
         )
         values
         (
          #{saleEntity.gid},
          #{saleEntity.nums}
         )
    </insert>

</mapper>

3.8 编写Service层接口:buyGoodsService.java

package com.wind.service;


import org.springframework.stereotype.Service;

@Service
public interface buyGoodsService {

    /**
     * 购买商品的方法。其中:goodsId:购买的商品ID。nums:购买的商品数量。
     */
    void buy(Integer goodsId, Integer nums);
}

3.9 编写Service层接口的实现类(使用Spring事务管理的注解):buyGoodsServiceImpl.java

package com.wind.serviceImpl;

import com.wind.dao.GoodsDao;
import com.wind.dao.SaleDao;
import com.wind.entity.GoodsEntity;
import com.wind.entity.SaleEntity;
import com.wind.exception.NotEnoughException;
import com.wind.service.buyGoodsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
public class buyGoodsServiceImpl implements buyGoodsService {

    @Autowired
    private SaleDao saleDao;

    @Autowired
    private GoodsDao goodsDao;


    /***
     * 另:在方法上直接使用注解 @Transactional 即可。
     * 解释:使用的是事务控制的默认值:
     * (1)@Transactional 只能加在public修改的方法上。
     * (2)默认的事务传播行为是:Propagation.REQUIRED
     * (3)默认是事务隔离级别是:Isolation.DEFAULT
     * (4)默认是在抛出运行时异常时回滚事务
     */

    /**
     * (0)@Transactional 只能加在public修改的方法上。
     * (1)事务的传播行为。
     * (2)事务的隔离级别。
     * (3)该事务是否是只读。
     * (4)rollbackFor:表示该方法发生执行的异常时一定会回滚。
     * <p>
     * 另外:rollbackFor 表示该方法发生执行的异常时一定会回滚,它的处理逻辑是:
     * (1)Spring框架会首先检查程序抛出的"异常"是否包含在你自己定义的rollbackFor的属性值中,
     * 如果在属性值列表中,不管什么异常,一定回滚。
     * (2)如果程序抛出的异常不在你自己定义的rollbackFor的属性值列表中,
     * Spring框架会检查这个异常是否是运行时异常RuntimeException,如果是,则一定回滚。
     */
    @Transactional(
            propagation = Propagation.REQUIRED,
            isolation = Isolation.DEFAULT,
            readOnly = false,
            rollbackFor = {
                    NullPointerException.class, NotEnoughException.class
            }
    )
    @Override
    public void buy(Integer goodsId, Integer nums) {

        System.out.println("。。。buy方法开始执行了。。。");

        //数据库操作1
        //1.购买了商品后,首先需要记录购买记录
        SaleEntity saleEntity = new SaleEntity();
        saleEntity.setGid(goodsId);
        saleEntity.setNums(nums);
        int insertSale = saleDao.insertSale(saleEntity);

        //2.然后需要更新库存了
        GoodsEntity goodsEntity = goodsDao.queryGoods(goodsId);
        if (goodsEntity == null) {
            //商品不存在
            throw new NullPointerException("编号是" + goodsId + "的商品不存在");

        } else if (goodsEntity.getAccount() < nums) {
            throw new NotEnoughException("编号是" + goodsId + "的商品库存不足");
        }

        //数据库操作2
        //3.更新库存了
        GoodsEntity entityNew = new GoodsEntity();
        entityNew.setId(goodsId);
        entityNew.setAccount(nums);
        int updateGoods = goodsDao.updateGoods(entityNew);

        System.out.println("。。。buy方法执行结束了。。。");
    }
}

3.10 编写一个运行时异常类:NotEnoughException.java

package com.wind.exception;

import org.springframework.stereotype.Component;


/**
 * 自定义的运行时异常
 */
@Component
public class NotEnoughException extends RuntimeException {

    public NotEnoughException() {
        super();
    }

    public NotEnoughException(String message) {
        super(message);
    }
}

3.11 编写测试基类:BaseTest.java

package test;

import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:config/*.xml"})
public abstract class BaseTest {
}

3.12 编写测试类:buyServiceTest.java

package test;


import com.wind.service.buyGoodsService;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;

public class buyServiceTest extends BaseTest {

    @Autowired
    private buyGoodsService buyGoodsService;

    @Test
    public void buyTest() {
        Integer goodsId = 2;
        Integer nums = 1;
        //service代理对象是=com.sun.proxy.$Proxy35:这是一个JDK动态代理对象
        System.out.println("service代理对象是=" + buyGoodsService.getClass().getName());
        buyGoodsService.buy(goodsId, nums);
    }
}

3.12.1 程序正常运行不抛出异常时的测试结果

3.12.2 程序抛出“商品不存在”的运行时异常的测试结果

3.12.3 程序抛出“库存不足”的运行时异常的测试结果

3.12.4 Spring中的事务管理器的确发生作用了, 看表中数据的主键ID

3.12.5 附录2个实体类:SaleEntity和GoodsEntity

package com.wind.entity;
import org.springframework.stereotype.Component;
import java.io.Serializable;
@Component
public class GoodsEntity implements Serializable {

    private static final long serialVersionUID = -959529746052167108L;

    private Integer id;
    private String name;
    private Integer account;
    private Integer price;

}

package com.wind.entity;
import org.springframework.stereotype.Component;
import java.io.Serializable;
@Component
public class SaleEntity implements Serializable {

    private static final long serialVersionUID = -959529746052167108L;

    private Integer id;
    private Integer gid;
    private Integer nums;
    
}

3.12.6 附录项目结构图

3.12.7 使用 Spring 的事务注解管理事务(掌握@Transactional )

通过@Transactional 注解方式,可将事务织入到相应 public 方法中,实现事务管理。@Transactional 的所有可选属性如下所示:

(1)propagation:用于设置事务传播属性。该属性类型为 Propagation 枚举,默认值为 Propagation.REQUIRED。

(2)isolation:用于设置事务的隔离级别。该属性类型为 Isolation 枚举,默认值为 Isolation.DEFAULT。

(3)readOnly:用于设置该方法对数据库的操作是否是只读的。该属性为boolean,默认值为 false。

(4)timeout:用于设置本操作与数据库连接的超时时限。单位为秒,类型为 int,默认值为-1,即没有时限。

(5)rollbackFor:指定需要回滚的异常类。类型为 Class[],默认值为空数组。 当然,若只有一个异常类时,可以不使用数组。

(6)rollbackForClassName:指定需要回滚的异常类类名。类型为 String[],默 认值为空数组。当然,若只有一个异常类时,可以不使用数组。

(7)noRollbackFor:指定不需要回滚的异常类。类型为 Class[],默认值为空数 组。当然,若只有一个异常类时,可以不使用数组。

(8)noRollbackForClassName:指定不需要回滚的异常类类名。类型为 String[], 默认值为空数组。当然,若只有一个异常类时,可以不使用数组。

需要注意的是:@Transactional 若用在方法上,只能用于 public 方法 上。对于其他非 public 方法,如果加上了注解@Transactional,虽然 Spring 不会报错,但不会将指定事务织入到该方法中。因为 Spring 会忽略掉所有非 public 方法上的@Transaction 注解。若@Transaction 注解在类上,则表示该类上所有的方法均将在执行时织入事务。

4、Spring中事务环境搭建(使用AspectJ的事务管理机制做代码演示)

4.1 在pom.xml文件中引入AspectJ管理事务的依赖:只需关注“第二种”即可

【注意:使用Spring自带的事务管理器只需要关注“第一种”即可。】

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.wind</groupId>
    <artifactId>ssm-web-tx2-aspectj</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <name>ssm-web Maven Webapp</name>
    <!-- FIXME change it to the project's website -->
    <url>http://www.example.com</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <spring.version>5.3.2</spring.version>
        <mybatis.version>3.4.6</mybatis.version>
        <mybatis.spring.version>2.0.3</mybatis.spring.version>
        <mysql.version>8.0.22</mysql.version>
        <druid.version>1.2.4</druid.version>
    </properties>

    <dependencies>

        <!--使用Spring管理事务需要的依赖如下,2选1即可。
        (1)第一种:使用Spring自带的事务管理器需要的依赖。
        (2)第二种:使用AspectJ管理事务需要的依赖。
        -->

        <!--第二种:使用AspectJ管理事务需要的依赖=begin-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.1.4.RELEASE</version>
        </dependency>
        <!--第二种:使用AspectJ管理事务需要的依赖=end-->


        <!--第一种:使用Spring自带的事务管理器需要的依赖=begin-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <!--第一种:使用Spring自带的事务管理器需要的依赖=end-->


        <!--SpringMVC依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <!--SpringMVC使用的Servlet依赖-->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
        </dependency>

        <!--SpringMVC使用的jsp依赖-->
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.2.1-b03</version>
        </dependency>

        <dependency>
            <groupId>jstl</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>

        <!--MyBatis依赖-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>${mybatis.version}</version>
        </dependency>

        <!--MyBatis与SpringMVC整合时需要的依赖-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>${mybatis.spring.version}</version>
        </dependency>

        <!--mysql驱动依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
        </dependency>

        <!--mysql数据库连接池依赖:使用的是德鲁伊数据库连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>${druid.version}</version>
        </dependency>

        <!--springMVC序列化用的jackson依赖-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.10.2</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.10.2</version>
        </dependency>

        <!--单元测试依赖-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.2.5.RELEASE</version>
            <scope>test</scope>
        </dependency>

    </dependencies>


    <build>
        <finalName>ssm-web</finalName>
        <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
            <plugins>
                <plugin>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.1</version>
                    <configuration>
                        <source>${maven.compiler.source}</source>
                        <target>${maven.compiler.target}</target>
                    </configuration>
                </plugin>
                <plugin>
                    <artifactId>maven-clean-plugin</artifactId>
                    <version>3.1.0</version>
                </plugin>
                <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
                <plugin>
                    <artifactId>maven-resources-plugin</artifactId>
                    <version>3.0.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.8.0</version>
                </plugin>
                <plugin>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <version>2.22.1</version>
                </plugin>
                <plugin>
                    <artifactId>maven-war-plugin</artifactId>
                    <version>3.2.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-install-plugin</artifactId>
                    <version>2.5.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-deploy-plugin</artifactId>
                    <version>2.8.2</version>
                </plugin>
            </plugins>
        </pluginManagement>

        <!--
        maven默认扫描src/main/java中的文件而不理会src/main/resources中的xml文件,
        所以后来添加了resource节点,这样就将src/main/resources中的xml文件改变maven默认的扫描策略,
        防止造成src/main/resources下的配置文件打包丢失。
        编译之后的文件中少了mapper.xml,这个和maven有关,maven编译src/java代码的时候,
        默认只会对java文件进行编译然后放在target/classes目录,需要在pom.xml中加入下面配置-->
        <!--如果不添加此节点,mapper.xml文件、config.properties文件、config.spring文件等
        都不会被加载到target的classes中去,也就不能被使用,也就会报错。-->
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
            </resource>
        </resources>
    </build>

</project>

4.2 在spring.xml文件中配置AspectJ事务管理器和AOP切入点表达式

只需要关注的是下面这三个:

<!--1.声名一个事务管理器对象-->
<!-- 2.声名业务方法它的事务属性(事务隔离级别、事务传播行为、事务超时时间)。 
(1)id:标识一个 <tx:advice> 与 </tx:advice> 之间的配置的内容的。 
(2)transaction-manager:事务管理器对象的ID。 -->
<!--3.上述的步骤2中配置好了哪些方法要使用AspectJ管理的事务, 但是这些仅仅只是方法名称而已,你并不指导这些方法是哪个包下的,确定不了, 
因此还需要配置AOP,使用切面表达式来指定具体的哪个包下的哪个方法要使用事务。 -->
<?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" xmlns:aop="http://www.springframework.org/schema/aop"
       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 http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--Spring的配置文件,用来声名service、dao、工具类等对象-->

    <!--加载连接mysql时需要的配置文件-->
    <context:property-placeholder location="classpath:config/jdbc.properties"/>

    <!--声名数据源,连接数据库-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
        <!--配置数据库连接池的初始化大小、最小、最大-->
        <property name="initialSize" value="5"/>
        <property name="minIdle" value="5"/>
        <property name="maxActive" value="20"/>
        <!--配置获取连接等待超时的时间,单位是毫秒-->
        <property name="maxWait" value="10000"/>
        <!--配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒-->
        <property name="timeBetweenEvictionRunsMillis" value="60000"/>
        <!--配置一个连接在池中最小生存的时间,单位是毫秒-->
        <property name="minEvictableIdleTimeMillis" value="300000"/>
    </bean>

    <!--声名一个SqlSessionFactoryBean,用它来创建SqlSessionFactory-->
    <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="configLocation" value="classpath:config/mybatis.xml"/>
    </bean>

    <!--声名MyBatis的扫描器,创建dao接口接口对象-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"/>
        <property name="basePackage" value="com.wind.dao"/>
    </bean>

    <!--声名service的注解@Service所在的包名-->
    <!--<context:component-scan base-package="com.wind.service,com.wind.serviceImpl"/>-->
    <context:component-scan base-package="com.wind.service*"/>

    <!--【1】事务的配置:下面是使用Spring自带的事务管理器来管理事务-->
    <!--&lt;!&ndash;1.声名一个事务管理器对象&ndash;&gt;-->
    <!--<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">-->
    <!--&lt;!&ndash;给事务管理器配置一个数据源,告诉Spring你需要管理的是这个数据库&ndash;&gt;-->
    <!--<property name="dataSource" ref="dataSource"/>-->
    <!--</bean>-->

    <!--&lt;!&ndash;2.开启事务注解驱动:告诉Spring使用注解管理事务,创建代理对象。-->
    <!--transaction-manager:事务管理器对象的ID。&ndash;&gt;-->
    <!--<tx:annotation-driven transaction-manager="transactionManager"/>-->


    <!--【2】事务的配置:下面是使用Spring集成的AspectJ的事务管理器来管理事务,完全是声名式事务,不需要侵入到代码中去-->
    <!--1.声名一个事务管理器对象-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--给事务管理器配置一个数据源,告诉Spring你需要管理的是这个数据库-->
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--
    2.声名业务方法它的事务属性(事务隔离级别、事务传播行为、事务超时时间)。
    (1)id:标识一个 <tx:advice> 与 </tx:advice> 之间的配置的内容的。
    (2)transaction-manager:事务管理器对象的ID。
    -->
    <tx:advice id="myAdvice" transaction-manager="transactionManager">
        <!--
        (1)<tx:attributes> :配置事务属性。
        (2)<tx:method:给具体的方法配置事务属性,method可以有多个,分别给不同的方法配置事务属性。
        (3)name:方法名称:
            3.1)可以是完整的方法名称,不带有包名和类名的。
            3.2)可以是通配符 * :标识任意字符。
         (4)propagation:事务的传播行为,是个枚举值。
         (5)isolation:事务的隔离级别。
         (6)timeout:事务的超时时间。
         (7)rollback-for:你指定的异常类的类名,全限定类名,发生异常了则一定回滚。
        -->
        <tx:attributes>
            <!--1.本项目中的案例走这个配置-->
            <tx:method name="buy"
                       propagation="REQUIRED"
                       isolation="DEFAULT"
                       rollback-for="java.lang.NullPointerException,com.wind.exception.NotEnoughException"/>

            <!--2.增加方法的事务配置:使用通配符 * -->
            <tx:method name="add*" propagation="REQUIRED" isolation="DEFAULT"/>

            <!--3.删除方法的事务配置-->
            <tx:method name="remove*" propagation="REQUIRED" isolation="DEFAULT"/>

            <!--4.来个默认的全局的方法-->
            <tx:method name="*" propagation="SUPPORTS" read-only="true"/>
        </tx:attributes>
    </tx:advice>

    <!--3.上述的步骤2中配置好了哪些方法要使用AspectJ管理的事务,
    但是这些仅仅只是方法名称而已,你并不指导这些方法是哪个包下的,确定不了,
    因此还需要配置AOP,使用切面表达式来指定具体的哪个包下的哪个方法要使用事务。
    -->
    <aop:config>
        <!--3.1 配置切入点表达式:
            (1)id:切入点表达式的名称,唯一值。
            (2)expression:切入点表达式,用来指定哪些类需要使用事务,AspectJ会为它创建代理对象。
        -->
        <aop:pointcut id="servicePT" expression="execution(* *..service..*.*(..))"/>

        <!--3.2 配置增强器:用来关联advice和pointcut
        (1)advice-ref:通知的ID,也就是 <tx:advice> 的ID值。
        (2)pointcut-ref:切入点表达式的ID。
        含义:当切入点表达式【servicePT】中切入的方法和通知【myAdvice】中配置的方法匹配时,即会对该方法进行事务管理。
        -->
        <aop:advisor advice-ref="myAdvice" pointcut-ref="servicePT"/>

    </aop:config>

</beans>

5、完结撒花-项目结构

猜你喜欢

转载自blog.csdn.net/cmm0401/article/details/112142174