声明式事务

JDBCTemplate

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O27BTim6-1640144673736)(.\img\009.jpg)]

xml配置 JDBCTemplate
<!--对JdbcTemplate进行IOC-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <!--注入数据源对象-->
    <property name="dataSource" ref="dataSource"/>
</bean>
装配JDBCTemplate
 @Autowired
    private JdbcTemplate jdbcTemplate;
增改查操作
String sql = "insert into t_emp (emp_name,emp_salary) values (?,?)";
    jdbcTemplate.update(sql,"aobama",3000d);

String sql = "update t_emp set emp_name=?,emp_salary=? where emp_id=?";
    jdbcTemplate.update(sql,"aolafu",6000d,1);

//查询员工的个数
    String sql = "select count(emp_id) from t_emp";
    Long count = jdbcTemplate.queryForObject(sql, Long.class);
    System.out.println(count);
查询返回实体类

封装实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Employee {
    private Integer empId;
    private String empName;
    private Double empSalary;
}

借助RowMapper完成查询

​ 在查询返回单列数据的时候,就该使用这个RowMapper

//查询多个员工信息,封装到List<Employee>中
    String sql = "select emp_id empId,emp_name empName, emp_salary empSalary from t_emp";
    List<Employee> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Employee.class));

    System.out.println(list);

声明式事务的概述

配置事务管理器

在spring的xml配置文件中我们需要配置事务管理器

<!--对事务管理者做IOC配置-->
<!--       
要开启注解方式声明式事务的支持
transaction-manager属性:表示使用哪个事务管理器对象来管理事务
如果你的事务管理器的id是transactionManager的话,可以省略该属性
-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!--注入数据源-->
    <property name="dataSource" ref="dataSource"/>
</bean>

注意:导入名称空间时有好几个重复的,我们需要的是 tx 结尾的那个。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2pkFDsR3-1640144673739)(.\img\008.jpg)]

在需要事务的方法上使用注解@Transactional就可以使用事务功能了

@Transactional
@Override
public void transfer(Integer fromId, Integer toId, Double money) {
    //1. 转出账户扣款
    accountDao.updateAccountMoney(fromId,-money);

    int num = 10/0;
    //2. 转入账户收款
    accountDao.updateAccountMoney(toId,money);
}

debug查看事务管理器中的关键方法

事务属性

只读属性

对一个查询操作来说,如果我们把它设置成只读,就能够明确告诉数据库,这个操作不涉及写操作。这样数据库就能够针对查询操作来进行优化。但是如果你的方法中执行写操作,那么就会报错

@Transactional(read-only=true)//read-only属性表示事务是否只读:默认值是false,如果设置为true那么当前事务中只能做数据库的读操作,不能做写操作
@Override
public void transfer(Integer fromId, Integer toId, Double money) {
    //1. 转出账户扣款
    accountDao.updateAccountMoney(fromId,-money);

    //int num = 10/0;
    //2. 转入账户收款
    accountDao.updateAccountMoney(toId,money);
}

如果在设置了只读的事务中进行写操作会抛出下面异常:

Caused by: java.sql.SQLException: Connection is read-only. Queries leading to data modification are not allowed

如果将@Transactional注解放在类上

@Transactional注解在类级别标记,会影响到类中的每一个方法。同时,类级别标记的@Transactional注解中设置的事务属性也会延续影响到方法执行时的事务属性。除非在方法上又设置了@Transactional注解。

,离它最近的@Transactional注解中的事务属性设置生效。

PS:Spring 环境下很多场合都有类似设定,一个注解如果标记了类的每一个方法那么通常就可以提取到类级别。但是,如果不是类中的所有方法都需要用到事务,则绝不允许将@Transaction注解放在类上

超时属性

事务在执行过程中,有可能因为遇到某些问题,导致程序卡住,从而长时间占用数据库资源。而长时间占用资源,大概率是因为程序运行出现了问题(可能是Java程序或MySQL数据库或网络连接等等)。

此时这个很可能出问题的程序应该被回滚,撤销它已做的操作,事务结束,把资源让出来,让其他正常程序可以执行。

概括来说就是一句话:超时回滚,释放资源。

回滚和不回滚的异常属性

默认情况
@Transactional
@Override
public void transfer(Integer fromId, Integer toId, Double money) throws ClassNotFoundException{
    //1. 转出账户扣款
    accountDao.updateAccountMoney(fromId,-money);
    //什么是运行时异常:不需要在编译时处理的异常
    //什么是编译时异常:需要在编译时就进行处理的异常
	//默认情况是遇到运行时异常才回滚:
    Class.forName("com.mysql.jdbc.Driveraaaaaa");
    int num = 10/0;
    //2. 转入账户收款
    accountDao.updateAccountMoney(toId,money);
}
设置回滚的异常
  • rollbackFor属性:需要设置一个Class类型的对象
  • rollbackForClassName属性:需要设置一个字符串类型的全类名
@Transactional(rollbackFor = Exception.class)
设置不回滚的异常

在默认设置和已有设置的基础上,再指定一个异常类型,碰到它不回滚。

@Transactional(
    noRollbackFor = FileNotFoundException.class
)
如果回滚和不回滚异常同时设置
当两者范围不同

不管是哪个设置范围大,都是在大范围内在排除小范围的设定。例如:

  • rollbackFor = Exception.class
  • noRollbackFor = FileNotFoundException.class

意思是除了 FileNotFoundException 之外,其他所有 Exception 范围的异常都回滚;但是碰到 FileNotFoundException 不回滚。

事务隔离级别属性

事务特性

事务包括四大特性:ACID

  • A:原子性:事务是最小的工作单元,不可再分。
  • B:一致性:事务必须保证多条DML语句同时成功或者同时失败。
  • C:隔离性:事务A与事务B之间具有隔离。
  • D:持久性:持久性说的是最终数据必须持久化到硬盘文件中,事务才算成功的结束。
事务隔离级别

事务隔离性村子啊隔离级别,理论上隔离级别包括4个:

  • 第一级别:读未提交(READ UNCOMMITTED):对方事务还没有提交,我们那当前事务就可以读取到对方未提交的数据。读未提交存在脏读现象:表示读到了脏的数据。
  • 第二级别:读已提交(READ COMMITTED):对方事务提交之后的数据我方可以读取到。这种隔离级别解决了脏读现象。存在的问题:不可重复读。
  • 第三级别:可重复读(REPEATABLE READ)这种隔离级别解决了:不可重复读问题。存在问题:读取到的数据是幻象。
  • 第四级别:序列化/串行话读(SERIALIZABLE),解决所有的问题,但效率低。需要事务排队。
级别 名字 隔离级别 脏读 不可重复读 幻读 数据库默认隔离级别
1 读未提交 read uncommitted
2 读已提交 read committed Oracle
3 可重复读 repeatable read MySQL
4 串行化 serializable 最高的隔离级别
设置方式

在 @Transactional 注解中使用 isolation 属性设置事务的隔离级别。 取值使用 org.springframework.transaction.annotation.Isolation 枚举类提供的数值。

@Transactional(isolation = Isolation.READ_UNCOMMITTED)

事务传播行为属性

事务的传播行为要研究的是是当两个方法嵌套执行的时候,外层方法的事务能否传播到内层方法以及怎么传播到外层方法

propagation属性

@Transactional 注解通过 propagation 属性设置事务的传播行为。它的默认值是:

Propagation propagation() default Propagation.REQUIRED;

propagation 属性的可选值由 org.springframework.transaction.annotation.Propagation 枚举类提供:

名称 含义
REQUIRED 默认值 当前方法必须工作在事务中 如果当前线程上有已经开启的事务可用,那么就在这个事务中运行 如果当前线程上没有已经开启的事务,那么就自己开启新事务,在新事务中运行 所以当前方法有可能和其他方法共用事务 在共用事务的情况下:当前方法会因为其他方法回滚而受连累
REQUIRES_NEW 建议使用 当前方法必须工作在事务中 不管当前线程上是否有已经开启的事务,都要开启
在业务层中声明两个内层方法
@Transactional(propagation = Propagation.REQUIRED)

测试 REQUIRED 模式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5kx2tTwm-1640144673741)(./img/015.png)]

效果:内层方法A、内层方法B所做的修改都没有生效,总事务回滚了。

过滤器或拦截器等类似组件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MQSnTQAU-1640144673744)(./img/018.png)]

Spring5的新特性

相关注解

注解名称 含义 可标记位置
@Nullable 可以为空 @Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})
@NonNull 不应为空 @Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})
@NonNullFields 在特定包下的字段不应为空 @Target(ElementType.PACKAGE) @TypeQualifierDefault(ElementType.FIELD)
@NonNullApi 参数和方法返回值不应为空 @Target(ElementType.PACKAGE) @TypeQualifierDefault({ElementType.METHOD, ElementType.PARAMETER})

整合junit5

导入依赖

在原有环境基础上增加如下依赖:

<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <version>5.7.0</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.3.1</version>
</dependency>
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = AtguiguSpringConfiguration.class)

使用复合注解

@SpringJUnitConfig(AtguiguSpringConfiguration.class)

总结

1 、Spring 框架概述
(1)轻量级开源 JavaEE 框架,为了解决企业复杂性,两个核心组成:IOC 和 AOP
(2)Spring5.2.6 版本
2 、IOC 容器
(1)IOC 底层原理(工厂、反射等)
(2)IOC 接口(BeanFactory)
(3)IOC 操作 Bean 管理(基于 xml)
(4)IOC 操作 Bean 管理(基于注解)
3 、Aop
(1)AOP 底层原理:动态代理,有接口(JDK 动态代理),没有接口(CGLIB 动态代理)
(2)术语:切入点、增强(通知)、切面
(3)基于 AspectJ 实现 AOP 操作
4 、JdbcTemplate
(1)使用 JdbcTemplate 实现数据库 curd 操作
(2)使用 JdbcTemplate 实现数据库批量操作
5 、事务管理
(1)事务概念
(2)重要概念(传播行为和隔离级别)
(3)基于注解实现声明式事务管理
(4)完全注解方式实现声明式事务管理
6 、Spring5 新功能
(1)整合日志框架
(2)@Nullable 注解
(3)函数式注册对象
(4)整合 JUnit5 单元测试框架
(5)SpringWebflux 使用

上一篇:Spring中事务失效问题


下一篇:spring事务的传播属性与隔离级别