Spring (三) 之Aop及事务控制

news2024/11/26 7:42:27

在这里插入图片描述

文章目录

    • 目标
  • 一、AOP 思想和重要术语(理解)
    • 1、需求问题
    • 2、AOP
    • 3、AOP 术语
  • 二、AOP 实现及 Pointcut 表达式(了解)
    • 1、AOP 规范及实现
    • 2、AspectJ
    • 3、AspectJ 切入点语法(掌握)
      • 3.1、切入点语法通配符
      • 3.2、切入点语法案例
  • 三、使用 XML 配置 AOP(掌握)
    • 1、需求
    • 2、使用 AOP
      • 2.1、拷贝之前动态代理的项目
      • 2.2、添加依赖
      • 2.3、编写 AOP 配置
      • 2.4、修改单元测试类
    • 3、变更使用 CGLIB
  • 四、使用注解配置 AOP(掌握)
    • 1、拷贝之前使用 XML 配置 AOP 的项目
    • 2、对比 XML 配置来使用注解的方式来配置
    • 3、修改配置文件
    • 4、变更使用 CGLIB
  • 五、集成准备(掌握)
    • 1、集成作用及本质
    • 2、项目搭建
      • 2.1、修改项目打包方式
      • 2.2、编写 web.xml
      • 2.3、设置编译版本及添加依赖和插件
      • 2.4、准备数据库
      • 2.5、编写实体类
  • 六、配置 SqlSessionFactory(掌握)
    • 1、使用 MyBatis 框架的问题
    • 2、拷贝配置文件
    • 3、配置 SqlSessionFactory
  • 七、配置 Mapper 对象(掌握)
    • 1、编写 AccountMapper 接口和 AccountMapper.xml
    • 2、使用 MyBatis 框架的问题
    • 3、配置 AccountMapper 对象(了解)
  • 八、配置业务层对象(掌握)
    • 1、编写业务接口及其实现类
    • 2、配置业务对象
    • 3、编写单元测试类
  • 九、简化配置(掌握)
    • 1、配置 Mapper 接口扫描器
    • 2、使用注解方式配置业务对象
    • 3、配置第三方解析程序
  • 十、银行转账案例分析(了解)
    • 1、模拟代码运行时出了问题
    • 2、出现问题的原因
    • 3、解决转账的事务问题
  • 十一、Spring 对事务支持(了解)
    • 1、Spring 对象事务支持的 API
    • 2、Spring实现事务的方式
  • 十二、使用 XML 配置事务(了解)
    • 1、编写配置
    • 2、测试
  • 十三、事务增强的属性配置(掌握)
    • 1、事务方法属性配置
    • 2、通用事务配置
  • 十四、使用注解配置事务(掌握)
    • 1、为业务类贴注解
    • 2、编写配置
    • 3、测试
    • 4、Transactional 注解说明
  • 上一篇: [Spring (二) 注解及代理模式:](https://blog.csdn.net/qq_45525848/article/details/137375910)
  • 下一篇: [Spring (四) 之配置及配置文件的操作:](https://blog.csdn.net/qq_45525848/article/details/137724031?csdn_share_tail=%7B%22type%22:%22blog%22,%22rType%22:%22article%22,%22rId%22:%22137724031%22,%22source%22:%22qq_45525848%22%7D)

目标

  • 理解 AOP 是什么,为什么使用 AOP,Spring 与它是什么关系。
  • 理解为什么要 Spring 集成 MyBatis,集成本质是什么,利用 Spring 什么功能做什么。
  • 掌握完成 Spring 集成 MyBatis。
  • 了解 Spring 对事务的支持体验在哪里。
  • 掌握利用 AOP 给业务方法添加事务。

一、AOP 思想和重要术语(理解)

1、需求问题

在开发中,为了给业务方法中增加日志记录,权限检查,事务控制等功能,此时我们需要去修改业务方法代码,考虑到代码的重用性,我们可以考虑使用 OOP 的继承或组合关系来消除重复,但是无论怎么样,我们都会在业务方法中纵向地增加这些功能方法的调用代码。此时,既不遵循开闭原则,也会为后期系统的维护带来很大的麻烦。(即不管怎样都得修改到原来的代码)

在这里插入图片描述

为了解决该问题,OOP 思想是不行了,得使用 AOP 思想。

2、AOP

AOP(Aspect Oriented Programming),是面向切面编程的技术,把一个个的横切关注点放到某个模块中去,称之为切面。那么每一个的切面都能影响业务的某一种功能,切面的目的就是功能增强,如日志切面就是一个横切关注点,应用中许多方法需要做日志记录的只需要插入日志的切面即可。(动态代理就可以实现 AOP),这种面向切面编程的思想就是 AOP 思想了。

在这里插入图片描述

AOP 能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。说人话:把业务方法中与业务无关的操作抽离到不同的对象中,最后使用动态代理的方式组合起来,动态地为类增加功能。

3、AOP 术语

  • Joinpoint:连接点,一般指需要被增强的方法。where:去哪里做增强。
  • Pointcut:切入点,哪些包中的哪些类中的哪些方法,可认为是连接点的集合。where:去哪些地方做增强。
  • Advice:增强,当拦截到 Joinpoint 之后,在方法执行的什么时机(when)做什么样(what)的增强。根据时机分为:前置增强、后置增强、异常增强、最终增强、环绕增强。
  • Aspect:切面,Pointcut + Advice,去哪些地方 + 在什么时候 + 做什么增强。
  • Target:被代理的目标对象。
  • Weaving:织入,把 Advice 加到 Target 上之后,创建出 Proxy 对象的过程。
  • Proxy:一个类被 AOP 织入增强后,产生的代理类。

二、AOP 实现及 Pointcut 表达式(了解)

1、AOP 规范及实现

AOP 的规范本应该由 SUN 公司提出,是被 AOP 联盟捷足先登。AOP 联盟制定 AOP 规范。

既然 AOP 是一个思想,就类似 IoC 思想,都是一个理论,那谁来实现这个 AOP 思想?AOP 思想的实现有 Spring AOP 和 AspectJ,都是开源的。那么如何选用呢,既然用 Spring 肯定要选用 Spring AOP,因为能与 Spring 无缝整合。那么为什么要使用提 AspectJ 呢? 那是因为用原始 Spring AOP 编程很麻烦,之后 Spring AOP 有些东西借鉴了 AspectJ(比如切入点表达式),简化 AOP 的配置与开发。

2、AspectJ

AspectJ 是一个面向切面的框架,它扩展了Java 语言(即使用 Java 对 AOP 进行了实现)。

3、AspectJ 切入点语法(掌握)

怎么表示 Pointcut,即怎么表示哪些包中的哪些类中的哪些方法?AspectJ 提供了表示切入点的语法。

在这里插入图片描述

3.1、切入点语法通配符

  • *:匹配任何部分,只能表示一个单词。
  • … : 可用于全限定名中和方法参数中,分别表示子包和 0 到 N 个参数。

3.2、切入点语法案例

注意第一个星符号后面有空格

execution(* cn.wolfcode.ssm.service.impl.*ServiceImpl.*(..))

三、使用 XML 配置 AOP(掌握)

1、需求

想给业务方法加模拟的事务功能,又不想修改原来的代码

2、使用 AOP

2.1、拷贝之前动态代理的项目

修改项目名为 spring-xml-aop 再导入项目,删除其中动态代理有关的代码。

2.2、添加依赖

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

2.3、编写 AOP 配置

在 applicationContext.xml,配置如下:

<!-- 配置真实对象 -->
<bean id="employeeService" class="cn.wolfcode.service.impl.EmployeeServiceImpl"/>

<!-- 配置是事务管理器, WHAT -->
<bean id="transactionManager" class="cn.wolfcode.tx.MyTransactionManager"/>

<!-- 配置 AOP -->
<aop:config>
    <aop:aspect ref="transactionManager">
        <!-- 定义切入点 WHERE -->
        <aop:pointcut expression="execution(* cn.wolfcode.service.impl.*ServiceImpl.*(..))" 
            id="txPointcut"/>
        <!-- 上面切入点切到方法,在方法执行之前,加  transactionManager.begin() -->
        <aop:before pointcut-ref="txPointcut" method="begin"/>
        <!-- 上面切入点切到方法,在方法正常执行完,加  transactionManager.commit() -->
        <aop:after-returning pointcut-ref="txPointcut" method="commit"/>
        <!-- 上面切入点切到方法,在方法执行抛出异常时,加  transactionManager.rollback() -->
        <aop:after-throwing pointcut-ref="txPointcut" method="rollback"/>
    </aop:aspect>
</aop:config>

2.4、修改单元测试类

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class EmployeeServiceTest {
	@Autowired
	private IEmployeeService employeeService;
	
	@Test
	public void testSave() {
		System.out.println(employeeService.getClass());
		employeeService.save("罗老师", "666");
	}
}

3、变更使用 CGLIB

Spring AOP 不做配置的话且目标对象实现接口的话,默认使用 JDK 的动态代理。若想强制使用 CGLIB,则在 applicationContext.xml 配置如下:

<aop:config proxy-target-class="true">
    <!-- 省略 ...... -->
</aop:config>

四、使用注解配置 AOP(掌握)

1、拷贝之前使用 XML 配置 AOP 的项目

修改项目名为 spring-anno-aop 再导入项目,删除其中配置 AOP 的 XML 代码。

2、对比 XML 配置来使用注解的方式来配置

package cn.wolfcode.tx;

@Component
@Aspect
public class MyTransactionManager {
	
	// 定义切入点 WHERE
	@Pointcut("execution(* cn.wolfcode.service.impl.*ServiceImpl.*(..))")
	public void txPointcut() {}
	
	@Before("txPointcut()")
	public void begin() {
		System.out.println("开启事务");
	}
	
	@AfterReturning("txPointcut()")
	public void commit() {
		System.out.println("提交事务");
	}
	
	@AfterThrowing("txPointcut()")
	public void rollback() {
		System.out.println("回滚事务");
	}
}

3、修改配置文件

在 applicationContext.xml,配置如下:

<!-- 配置真实对象 -->
<!-- 配置是管理器,WHAT -->
<context:component-scan base-package="cn.wolfcode"/>

<!-- AOP 注解解析器,让这些 AOP 注解起作用 -->
<aop:aspectj-autoproxy/>

4、变更使用 CGLIB

在 applicationContext.xml,配置如下

<aop:aspectj-autoproxy proxy-target-class="true"/>

五、集成准备(掌握)

1、集成作用及本质

  • 使用框架,在别人的基础上开发,提高效率;
  • 集成 MyBatis 和 业务层,即业务对象、Mapper 对象等都交由 Spring 容器管理:
    • 利用 MyBatis 做持久化。
    • 使用 Spring IoC 和 DI 来完成对象创建及其属性注入。
    • 后面再使用AOP来配置事务。

2、项目搭建

新建 Maven 项目,打包方式是 war,为后面项目做铺垫。需求:做个转账功能

2.1、修改项目打包方式

在 pom.xml,追加一段配置,配置如下:

<packaging>war</packaging>

2.2、编写 web.xml

手动在项目的 main 目录下新建 webapp/WEB-INF/web.xml,配置如下:

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
	version="3.0">
</web-app>

2.3、设置编译版本及添加依赖和插件

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
</properties>

<dependencies>
    <!-- 数据库驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.45</version>
        <scope>runtime</scope>
    </dependency>
    <!-- 数据库连接池 -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.9</version>
    </dependency>
    
    <!-- MyBatis 相关 -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.4.5</version>
    </dependency>
    <!-- Spring 集成 MyBatis 的依赖 -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>1.3.1</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.7.25</version>
    </dependency>

    <!-- Spring 相关 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.0.8.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.8.13</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.0.8.RELEASE</version>
    </dependency>

    <!-- 测试相关 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.0.8.RELEASE</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>

    <!-- web 项目共用 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.22</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.0.1</version>
        <scope>provided</scope>
    </dependency>

    <!-- 页面标签 -->
    <dependency>
        <groupId>org.apache.taglibs</groupId>
        <artifactId>taglibs-standard-spec</artifactId>
        <version>1.2.5</version>
    </dependency>
    <dependency>
        <groupId>org.apache.taglibs</groupId>
        <artifactId>taglibs-standard-impl</artifactId>
        <version>1.2.5</version>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.tomcat.maven</groupId>
            <artifactId>tomcat7-maven-plugin</artifactId>
            <version>2.1</version>
            <configuration>
                <port>80</port> <!-- 端口 -->
                <path>/</path> <!-- 上下路径 -->
                <uriEncoding>UTF-8</uriEncoding> <!-- 针对 GET 方式乱码处理 -->
            </configuration>
        </plugin>
    </plugins>
</build>

2.4、准备数据库

在数据库新建一个名为 ssm 的库,建如下的表:

CREATE TABLE `account` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `balance` decimal(10,2) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

2.5、编写实体类

package cn.wolfcode.domain;

@Setter
@Getter
public class Account {
	private Long id;
	private BigDecimal balance;
}

六、配置 SqlSessionFactory(掌握)

1、使用 MyBatis 框架的问题

public void testOld() throws Exception {
    SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
    SqlSession session = ssf.openSession();
    AccountMapper accountMapper = session.getMapper(AccountMapper.class);
    // ......
    session.close();
}

发现要创建 SqlSessionFactory 对象,而且应用中只需要一个,而创建对象这个工作交由 Spring 创建并管理多好。

2、拷贝配置文件

从给的课件中 practise/config,拷贝 db.properties、log4j.properties 和 mybatis-config.xml 到项目的 resources 目录下。

3、配置 SqlSessionFactory

查看 SqlSessionFactoryBean,其实现 FactoryBean 接口,通过配置在 Spring 配置文件中,会往容器里面创建和存放 SqlSessionFactory 类型对象。配置的时候想想配置些什么?且对比之前 MyBatis 主配置文件。

在 resources 目录下新建 applicationContext.xml,配置如下:

<!-- 关联 db.properties 文件 -->
<context:property-placeholder location="classpath:db.properties"/>

<!-- 配置 DataSource -->
<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}"/>	
</bean>

<!-- 配置 SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <!-- 数据源 -->
    <property name="dataSource" ref="dataSource"/>
    <!-- 配置别名,若在 Mapper XML 文件中不想使用别名也可以不用配置 -->
    <property name="typeAliasesPackage" value="cn.wolfcode.domain"/>
    <!-- 配管关联 MyBatis 主配置文件 可以不配置,后期使用 mybatis 插件的时候需要配置
        <property name="configLocation" value="classpath:mybatis-config.xml"></property>
    -->
    <!-- 配管关联 MyBatis Mapper XML 文件 ,因为两者编译之后在一起
        <property name="mapperLocations" value="classpath:cn/wolfcode/mapper/*Mapper.xml"></property>
    -->
</bean>

七、配置 Mapper 对象(掌握)

1、编写 AccountMapper 接口和 AccountMapper.xml

对应提供加钱和减钱的方法及对应 SQL。

package cn.wolfcode.mapper;

public interface AccountMapper {
	// 加钱
	void addBalance(@Param("inId")Long inId, @Param("amount")BigDecimal amount);
	// 减钱
	void subtractBalance(@Param("outId")Long outId, @Param("amount")BigDecimal amount);
}

在 resources 目录下新建 cn/wolfcode/mapper/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="cn.wolfcode.mapper.AccountMapper">
	<update id="addBalance">
		UPDATE account SET balance = balance + #{amount} WHERE id = #{inId}
	</update>
	<update id="subtractBalance">
		UPDATE account SET balance = balance - #{amount} WHERE id = #{outId}
	</update>
</mapper>

2、使用 MyBatis 框架的问题

public void testOld() throws Exception {
    SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
    SqlSession session = ssf.openSession();
    AccountMapper accountMapper = session.getMapper(AccountMapper.class);
    // ......
    session.close();
}

获取 Mapper 接口的代理对象之前是由 MyBatis 管理,那么带来的问题,若想在业务对象中注入 Mappe r接口的代理对象就,必须改成交由 Spring 容器统一管理。

3、配置 AccountMapper 对象(了解)

对比以前获取 AccountMapper 接口代理对象的代码思考配置。在 applicationContext.xml,配置如下:

<!-- 配置 AccountMapper -->
<bean id="accountMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
    <!-- 借由容器中的 sqlSessionFactory 来创建 -->
    <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    <!-- 具体创建什么接口类型的代理对象 -->
    <property name="mapperInterface" value="cn.wolfcode.mapper.AccountMapper"/>
</bean>

八、配置业务层对象(掌握)

1、编写业务接口及其实现类

package cn.wolfcode.service;

public interface IAccountService {
	// 转账方法
	void transfer(Long outId, Long inId, BigDecimal amount);
}
package cn.wolfcode.service.impl;

public class AccountServiceImpl implements IAccountService { 
    private AccountMapper accountMapper;
    public void setAccountMapper(AccountMapper accountMapper) {
        this.accountMapper = accountMapper;
    }

    @Override
    public void transfer(Long outId, Long inId, BigDecimal amount) { 
        accountMapper.subtractBalance(outId, amount);
        accountMapper.addBalance(inId, amount);
    }
}

2、配置业务对象

在 applicationContext.xml,配置如下:

<bean id="accountService" class="cn.wolfcode.service.impl.AccountServiceImpl">
    <property name="accountMapper" ref="accountMapper"/>
</bean>

3、编写单元测试类

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AccountServiceTest {
	@Autowired
	private IAccountService accountService;

	@Test
	public void testTransfer() {
		accountService.transfer(1L, 2L, BigDecimal.TEN);
	}
}

九、简化配置(掌握)

1、配置 Mapper 接口扫描器

之前配置多个 Mapper 接口代理对象是比较麻烦的,使用 Mapper 接口扫描器可以批量生成 Mapper 接口代理对象并注册到 Spring 容器中。

在 applicationContext.xml,删除原有单个配置 Mapper 接口代理对象的的代码,改配置如下:

<!-- 批量创建 Mapper 接口实现对象 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <!-- 指定 Mapper 接口所在的包路径 -->
    <property name="basePackage" value="cn.wolfcode.mapper"/>
</bean>

2、使用注解方式配置业务对象

在业务类上贴 IoC 注解和 DI 注解。

package cn.wolfcode.service.impl;

@Service
public class AccountServiceImpl implements IAccountService {
	@Autowired
	private AccountMapper accountMapper;

	@Override
	public void transfer(Long outId, Long inId, BigDecimal amount) {
		accountMapper.subtractBalance(outId, amount);
		accountMapper.addBalance(inId, amount);
	}
}

3、配置第三方解析程序

在 applicationContext.xml,配置如下:

<!-- 
    IoC DI 注解解析器
    让 Spring 帮你创建所有业务对象,给业务对象字段设置值
-->
<context:component-scan base-package="cn.wolfcode.service.impl"/>

十、银行转账案例分析(了解)

1、模拟代码运行时出了问题

在 AccountServiceImpl 中转账的方法,中间插入造成异常(模拟)的代码。再运行单元测试方法,发现转出人少了钱,但转入人没有收到钱。

package cn.wolfcode.service.impl;

@Service
public class AccountServiceImpl implements IAccountService {
	@Autowired
	private AccountMapper accountMapper;

	@Override
	public void transfer(Long outId, Long inId, BigDecimal amount) {
		accountMapper.subtractBalance(outId, amount);
		int i = 1/0;
		accountMapper.addBalance(inId, amount);
	}
}

2、出现问题的原因

在这里插入图片描述

3、解决转账的事务问题

所以只要把转入和转出放在同一个事务就可以解决,解决思路(AOP)如下:

在这里插入图片描述

十一、Spring 对事务支持(了解)

1、Spring 对象事务支持的 API

PlatformTransactionManager:统一抽象处理事务操作相关的方法,是其他事务管理器的规范,根据实际情况选用不同的实现类。

  • TransactionStatus getTransaction(TransactionDefinition definition):根据事务定义信息从事务环境中返回一个已存在的事务,或者创建一个新的事务。
  • void commit(TransactionStatus status):根据事务的状态提交事务,如果事务状态已经标识为 rollback-only,该方法执行回滚事务的操作。
  • void rollback(TransactionStatus status):将事务回滚,当 commit 方法抛出异常时,rollback 会被隐式调用。

使用 Spring 管理事务的时候,针对不同的持久化技术选用不同的事务管理器:

  • JDBC 和 MyBatis 使用 DataSourceTransactionManager
  • Hibernate 使用 HibernateTransactionManager。
  • JPA 使用 JpaTransactionManager。

2、Spring实现事务的方式

  • 编程式事务:通过编写代码来管理事务;
  • 声明式事务:通过XML配置或注解来管理事务。

十二、使用 XML 配置事务(了解)

1、编写配置

在 applicationContext.xml,配置如下:

<!-- 配置事务管理器 WHAT -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!-- 配置数据源 -->
    <property name="dataSource" ref="dataSource"/>
</bean>

<!-- 配置增强 WHEN,加关联上面 WHAT -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <!-- 这里可以针对不同方法进行差异化配置 -->
        <tx:method name="transfer"/>
    </tx:attributes>
</tx:advice>

<aop:config>
    <!-- 配置切入点 WHERE -->
    <aop:pointcut expression="execution(* cn.wolfcode.service.impl.*ServiceImpl.*(..))" id="txPoint"/>
    <!-- 关联上面的增强 -->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="txPoint"/>
</aop:config>

2、测试

再运行单元测试看下效果。

十三、事务增强的属性配置(掌握)

1、事务方法属性配置

在这里插入图片描述

  • name:匹配到的方法,必须配置;可以使用通配符 ,比如 get 匹配所有 get 开头的方法。
  • propagation:事务的传播规则。
  • isolation:代表数据库事务隔离级别(就使用默认)
    • DEFAULT:让 Spring 使用数据库默认的事务隔离级别;
    • 其他:Spring 模拟,一般不用。
  • timeout:缺省 -1,表示使用数据库底层的超时时间。
  • read-only:若为 true,开启一个只读事务,只读事务的性能较高,但是不能在只读事务中操作 DML。
  • no-rollback-for:如果遇到的异常是匹配的异常类型,就不回滚事务。
  • rollback-for:如果遇到的异常是指定匹配的异常类型,才回滚事务。

Spring 默认情况下,业务方法抛出 RuntimeException 及其子类的异常才会去回滚,而抛出 Exception 和 Thowable 的异常不会回滚(自定义的业务异常会去继承 RuntimeException)。

2、通用事务配置

在 applicationContext.xml,增加对业务方法的差异配置,配置如下:

<tx:attributes>
    <tx:method name="get*" read-only="true"/>
    <tx:method name="list*" read-only="true"/>
    <tx:method name="query*" read-only="true"/>
    <tx:method name="count*" read-only="true"/>
    <tx:method name="*"/>
</tx:attributes>
  • 其它方法的配置没有匹配到,就会使用最后一个通配方法配置匹配。
  • 查询的方法名其实相对有限的,而其业务的方法则无数,所以才像上面那么配置。
  • 方法名约定俗成,所以自己事务配置好了,业务方法名切记不要乱取了

十四、使用注解配置事务(掌握)

使用 XML 配置事务,配置过于繁琐。

1、为业务类贴注解

package cn.wolfcode.service.impl;

@Service
// 贴事务注解
@Transactional
public class AccountServiceImpl implements IAccountService {
	@Autowired
	private AccountMapper accountMapper;

	@Override
	public void transfer(Long outId, Long inId, BigDecimal amount) {
		accountMapper.subtractBalance(outId, amount);
		int i = 1/0;
		accountMapper.addBalance(inId, amount);
	}
}

2、编写配置

在 applicationContext.xml 删除之前 XML 配置事务的代码,只保留配置事务管理器,配置具体如下:

<!-- 配置事务管理器 WHAT -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

<!-- 事务注解解析器 -->
<tx:annotation-driven transaction-manager="transactionManager"/>

3、测试

再运行单元测试看下效果。

4、Transactional 注解说明

Transactional 注解可以贴多个地方:

  • 贴业务类或业务接口上,事务的配置是通用与整个类或接口的的方法;
  • 贴业务方法上,即方法上的的事务的配置仅限于被贴的方法;
  • 同时存在时,后者覆盖前者。

若想强制使用 CGLIB 动态代理,则修改事务注解解析器的配置,如下:

<tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager"/>

上一篇: Spring (二) 注解及代理模式:

下一篇: Spring (四) 之配置及配置文件的操作:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1606952.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux 网络基本命令

一、查看网络信息 ifconfig 二、关闭网络 ifdown ens33 (有的电脑不一定是ens33&#xff0c;具体看上图画线的地方) 三、开启网络 ifup ens33

【电路笔记】-数字逻辑门总结

数字逻辑门总结 文章目录 数字逻辑门总结1、概述2、逻辑门真值表3、总结 数字逻辑门有三种基本类型&#xff1a;与门、或门和非门。 1、概述 我们还看到&#xff0c;数字逻辑门具有与其相反或互补的形式&#xff0c;分别为“与非门”、“或非门”和“缓冲器”&#xff0c;并且…

RK3588 Android13 鼠标风格自定义动态切换

前言 电视产品,客户提供了三套鼠标图标过来,要求替换系统中原有丑陋风格且要支持动态切换, 并且在 TvSetting 中要有菜单,客户说啥就是啥呗,开整。 效果图 test framework 部分修改文件清单 png 为鼠标风格资源图片,这里就不提供了,可自由找一个替换一下就行 framew…

C语言野指针【入门详解】

目录 一、什么是野指针 二、野指针的成因 2.1 指针未初始化 2.2 指针越界访问 2.3 指针指向的空间释放 三、如何规避野指针 3.1 初始化指针 3.2 小心越界访问 3.3 当指针不用时&#xff0c;及时置为空 3.4 避免返回局部变量的地址 *结语&#xff1a; 希望这篇关于指…

[SWPUCTF 2021 新生赛]jicao、easy_md5

目录 一、[SWPUCTF 2021 新生赛]jicao 什么是JSON&#xff1f; JSON语法&#xff1a; [SWPUCTF 2021 新生赛]jicao 二、[SWPUCTF 2021 新生赛]easy_md5 PHP弱类型和强类型 1.弱类型比较&#xff08;&#xff09; 2.强类型比较&#xff08;&#xff09; [SWPUCTF 2021 …

OceanBase数据库日常运维快速上手

这里为大家汇总了从租户创建、连接数据库&#xff0c;到数据库的备份、归档、资源配置调整等&#xff0c;在OceanBase数据库日常运维中的操作指南。 创建租户 方法一&#xff1a;通过OCP 创建 确认可分配资源 想要了解具体可分配的内存量&#xff0c;可以通过【资源管理】功…

华为OD机试 - 结队编程(Java 2024 C卷 100分)

华为OD机试 2024C卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷C卷&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;每一题都有详细的答题思路、详细的代码注释、样例测试…

Google DeepMind: Many-Shot vs. Few-Shot

本文介绍了如何通过增大上下文窗口&#xff0c;利用大型语言模型&#xff08;LLMs&#xff09;进行多实例上下文学习&#xff08;Many-Shot In-Context Learning&#xff0c;ICL&#xff09;的方法。主要描述了现有的几实例上下文学习方法虽然在推理时能够通过少量例子学习&…

MicroSIP电话呼叫软件使用及配置方法

MicroSIP是一款开源的SIP协议电话软件&#xff0c;它可以帮助你在计算机上进行语音和视频通话。下面是关于如何使用和配置MicroSIP的一些基本步骤&#xff1a; 安装MicroSIP 从MicroSIP官方网站下载适合你操作系统的安装包23。 解压下载的文件&#xff0c;并运行安装程序。 …

可以与 FastAPI 不分伯仲的 Python 著名的 Web 框架

正如你所理解的&#xff0c;任何领域都不可能停止进步&#xff0c;不断使用相同的工具意味着不思进取。这一点在信息技术领域&#xff0c;尤其是网络开发行业非常明显。 关于网络框架&#xff0c;不论是 Django 和 Flask 等传统框架还是 Python 的新型高级框架&#xff0c;一直…

算法课程笔记——常用库函数

memset初始化 设置成0是可以每个设置为0 而1时会特别大 -1的补码是11111111 要先排序 unique得到的是地址 地址减去得到下标 结果会放到后面 如果这样非相邻 会出错 要先用sort排序 O&#xff08;n&#xff09;被O&#xff08;nlogn&#xff09;覆盖

【智能算法】饥饿游戏搜索算法(HGS)原理及实现

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.结果展示4.参考文献 1.背景 2021年&#xff0c;Yang等人受到自然界饥饿驱动的活动和动物的行为选择启发&#xff0c;提出了饥饿游戏搜索算法&#xff08;Hunger Games Search, HGS&#xff09;。 2.算法原理 2.1算法思想 HGS…

【Java网络编程】网络编程概述、UDP通信(DatagramPacket 与 DatagramSocket)

目录 1、网络编程 1.1、概述 1.1、网络编程三要素 2、UDP通信 2.1、DatagramPacket 与 DatagramSocket 2.1、 UDP发收数据示例 1、网络编程 1.1、概述 在网络通信协议下&#xff0c;不同计算机上运行的程序&#xff0c;进行的数据传输应用场景&#xff1a;即时通信、网游…

韩顺平Java | C27 正则表达式

入门介绍 需求&#xff1a;提取文本中某类字符 传统方法&#xff1a;遍历每个字符&#xff0c;判断其是否在ASCII码中某种类型得编码范围内&#xff0c;代码量大&#xff0c;效率不高 正则表达式(RegExp, regular expression)&#xff1a;处理文本的利器&#xff0c;是对字符…

网络编程套接字(三)之TCP服务器简单实现

目录 一、服务端TcpServer 1、tcp_server.hpp 2、tcp_server.cc 二、客户端TcpClient tcp_client.cc 三、服务器和客户端进行通信 四、完整代码 一、服务端TcpServer 首先我们需要对服务端进行封装。我们需要的成员变量有IP地址&#xff0c;端口号port&#xff0c;以及监…

49.基于SpringBoot + Vue实现的前后端分离-爱心公益网站系统(项目 + 论文)

项目介绍 本站是一个B/S模式系统&#xff0c;采用SpringBoot Vue框架&#xff0c;MYSQL数据库设计开发&#xff0c;充分保证系统的稳定性。系统具有界面清晰、操作简单&#xff0c;功能齐全的特点&#xff0c;使得基于SpringBoot Vue技术的爱心公益网站系统设计与实现管理工作…

Gartner发布信任、风险和安全管理领域的生成式人工智能创新指南:生成式AI整个生命周期运行中的攻击面

生成式人工智能带来了三类新风险&#xff1a;内容异常、数据保护和人工智能应用安全。使用或构建 GenAI 应用的 IT 领导者可以利用这项研究来了解市场动态并评估新兴的 GenAI TRiSM 技术和解决新风险的提供商。 主要发现 在企业应用中集成大语言模型&#xff08;LLM &#xff0…

《QT实用小工具·三十》基于QT开发的访客管理平台demo

1、概述 源码放在文章末尾 该项目为访客管理平台demo&#xff0c;包含主界面、系统设置、警情查询、调试帮助、用户退出功能。 项目部分代码如下&#xff1a; #ifndef QTHELPER_H #define QTHELPER_H#include "head.h"class QtHelper { public://获取所有屏幕区域…

Vue 3 项目构建与效率提升:vite-plugin-vue-setup-extend 插件应用指南

一、Vue3项目创建 前提是已安装Node.js&#xff08;点击跳转Node官网&#xff09; npm create vuelatest这一指令将会安装并执行 create-vue&#xff0c;它是 Vue 官方的项目脚手架工具。你将会看到一些诸如 TypeScript 和测试支持之类的可选功能提示&#xff1a; ✔ Projec…

InstantMesh:利用稀疏视图大规模重建模型从单张图像高效生成3D网格

作者&#xff1a;Jiale Xu&#xff0c;Weihao Cheng&#xff0c;Yiming Gao等 编译&#xff1a;东岸因为一点人工一点智能 InstantMesh&#xff1a;利用稀疏视图大规模重建模型从单张图像高效生成3D网格在这项工作中&#xff0c;我们提出了InstantMesh&#xff0c;一个开源的…