Spring学习(五)事务

文章目录

  • 事务
    • JdbcTemplate
      • 准备
    • 声明式事务概念
      • 概念
      • 代码式事务
      • 声明式事务
    • 基于注解的声明式事务
      • 准备
      • 案例
      • 加入事务
      • 事务属性
    • 失效场景
      • 访问权限
      • final, static修饰
      • 方法内部调用
      • 未被Spring管理
      • 多线程调用
      • 吞异常
      • 抛出别的异常
      • 自定义回滚异常
      • 嵌套事务回滚过多

事务

JdbcTemplate

Spring框架对JDBC进行了封装,使用JdbcTemplate易于实现对数据库的操作

准备

  1. 依赖
<!--spring jdbc-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>6.0.2</version>
</dependency>
<!--mysql-->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.30</version>
</dependency>
<!--数据源-->
<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.0.31</version>
</dependency>
  1. jdbc.properties
jdbc.user=root
jdbc.password=root
jdbc.url=jdbc:mysql://localhost:3306/spring
jdbc.driver=com.mysql.cj.jdbc.Driver
  1. bean.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:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd"><!--外部属性文件--><context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder><!--配置数据源--><bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="url" value="${jdbc.url}"></property><property name="driverClassName" value="${jdbc.driver}"></property><property name="username" value="${jdbc.user}"></property><property name="password" value="${jdbc.password}"></property></bean><!--配置jdbcTemplate--><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><!--装配数据源--><property name="dataSource" ref="druidDataSource"></property></bean></beans>
  1. 配置数据库
    在这里插入图片描述
  2. 增删改操作
@SpringJUnitConfig(locations = "classpath:bean.xml")
public class JdbcTemplateTest {@Autowiredprivate JdbcTemplate jdbcTemplate;//添加 修改 删除//这三个操作对数据项都使用JdbcTemplate的update方法@Testpublic void insertTest(){//1 sql语句String sql1 = "INSERT INTO t_emp VALUE(NULL,?,?,?)";//2 调用JdbcTemplate方法,传参//int row = jdbcTemplate.update(sql, "Max", 19, "f");//    orObject[] params = {"Fraud", 19, "f"};int row = jdbcTemplate.update(sql1,params);System.out.println(row);}@Testpublic void updateTest(){String sql2 = "UPDATE t_emp SET SEX = ? WHERE ID = ?";Object[] params = {"m", 3};int row = jdbcTemplate.update(sql2,params);System.out.println(row);}@Testpublic void deleteTest(){String sql3 = "delete from t_emp where id = ?";int row = jdbcTemplate.update(sql3,3);System.out.println(row);}
}
  1. 查询操作
//查询:返回对象
//queryForObject---获取一个对象
@Test
public void selectObject() {//1 自行封装,使用lamba//String sql = "select * from t_emp where id = ? ";//Emp empresult = jdbcTemplate.queryForObject(sql,//        (rs, rowNum) ->{//            Emp emp = new Emp();//            emp.setId(rs.getInt( "id"));//            emp.setName(rs.getString( "name"));//             emp.setAge(rs.getInt( "age"));//            emp.setSex(rs.getString("sex"));//        }, 1);//System.out.println(empresult);*///2 使用封装类String sql = "select * from t_emp where id = ?";Emp emp = jdbcTemplate.queryForObject(sql,new BeanPropertyRowMapper<>(Emp.class), 1);System.out.println(emp);
}
//查询:返回list集合
//query---获取一系列对象,存于List中
@Test
public void selectList() {String sql = "select * from t_emp";List<Emp> empList = jdbcTemplate.query(sql,new BeanPropertyRowMapper<>(Emp.class));System.out.println(empList);
}
//查询:返回单值
@Test
public void selectElement() {String sql = "select count(*) from t_emp";Integer count = jdbcTemplate.queryForObject(sql, Integer.class);System.out.println(count);
}

声明式事务概念

概念

  1. 事务(transaction)是访问并可能操作各种数据想的一个操作序列,要么全部执行要么全部不执行,作为不可分割的工作单位;
    事务由事务开始与事务结束之间执行的全部数据库操作组成;
  2. 特性

原子性:要么全部执行要么全部不执行,执行中发生错误则全部回滚;
一致性:事务执行前与执行后数据库处于一致性状态,事务成功则系统正确应用变化,处于有效状态;事务错误则回滚所有变化,回到原始状态;
隔离性:不同事务操纵相同数据时,各自拥有独立完整数据空间,并发事务之间的修改必须相互隔离。事务查看数据时,数据只会处于其余事务修改它之前,或完成修改之后的状态,不会处于变化过程中;
持久性:事务成功结束后,其对数据库产生的变化必须保存,即使系统崩溃,在重启后也能恢复到事务成功时的状态;

代码式事务

避免由于Spring AOP导致的事务失效问题,能够更小粒度地控制事务范围,且更加直观;
全部由代码完成,自行控制事务,细节无法屏蔽,繁琐,且代码复用性低;

声明式事务

抽取固定模式的代码进行封装,提高开发效率,减少冗余代码,由框架考虑各种问题,优化健壮性及性能等;

基于注解的声明式事务

准备

  1. 配置文件
 <!--扫描组件--><context:component-scan base-package="com.jobs.spring6.tx"></context:component-scan>
  1. 建表
    在这里插入图片描述
  2. 接口
    在这里插入图片描述

案例

BookDao
public interface BookDao {Integer getPrice(Integer bookId);void stockChange(Integer bookId);void balanceChange(Integer userId, Integer bookId);
}
BookDaoImpl
@Repository
public class BookDaoImpl implements BookDao {@Autowiredprivate JdbcTemplate jdbcTemplate;@Overridepublic Integer getPrice(Integer bookId) {String sql = "select price from t_book where book_id = ?";Integer price = jdbcTemplate.queryForObject(sql, Integer.class, bookId);return price;}@Overridepublic void stockChange(Integer bookId) {String sql = "update t_book set stock = stock - 1 where book_id = ?";jdbcTemplate.update(sql, bookId);}@Overridepublic void balanceChange(Integer userId, Integer bookId) {String sql = "update t_user set balance = balance - ? where user_id = ?";jdbcTemplate.update(sql, getPrice(bookId), userId);}
}
BookService
public interface BookService {void buyBook(Integer bookId, Integer userId);
}
BookServiceImpl
@Service
public class BookServiceImpl implements BookService {@Autowiredprivate BookDao bookDao;@Overridepublic void buyBook(Integer bookId, Integer userId) {bookDao.getPrice(bookId);bookDao.stockChange(bookId);bookDao.balanceChange(userId, bookId);}
}
BookController
@Controller
public class BookController {@Autowiredprivate BookService bookService;public void userBuyBook(Integer bookId, Integer userId) {bookService.buyBook(bookId, userId);}
}
BuyTest
@SpringJUnitConfig(locations = "classpath:bean.xml")
public class BuyTest {@Autowiredprivate BookController bookController;@Testpublic void buyBookTest(){bookController.userBuyBook(2, 1);}
}

加入事务

  1. 配置文件
xmlns:tx="http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd<bean id="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="name" ref="druidDataSource"></property></bean><!--transaction-manager属性的默认值为transactionManager若事务管理器Bean的id为此值,者可忽略此属性--><tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
  1. 因为Service层标识业务逻辑,一个方法代表一个完成的功能,因此处理事务一般在Service层处理
  2. 通过注解@Transactional标识的方法(该方法单独)或其类(其中所有方法),都会被事务管理器管理;
@Transactional
@Service
public class BookServiceImpl implements BookService {...}
// or
@Transactional
@Override
public void buyBook(Integer bookId, Integer userId) {...}

事务属性

  1. 只读—readOnly = true
    该事务操作不涉及写,则可以针对查询操作进行优化;
  2. 超时—timeout = ‘num’

num = -1:永不超时
num > 0:num秒内执行,超时则抛出异常

  1. 回滚策略

rollbackFor/rollbackForClassName:对指定异常种类进行回滚
noRollbackFor/noRollbackForClassName:对指定异常种类不进行回滚

  1. 隔离级别—isolation
    事务之间相互隔离并发运行的手段,隔离级别越高,数据一致性越好,但并发性越弱;
隔离级别脏读不可重复读幻读
读未提交:READ UNCOMMITTED
读已提交:READ COMMITTED
可重复读:REPEATABLE READ
串行化:SERIALIZABLE
  1. 传播行为—propagation
    例如a方法和b方法,其上都存在事务,当a方法执行时需要调用b方法,那么事务按照传播行为所规定的进行变化;
类型行为
REQUIRED没有事务就新建事务,存在事务则加入
SUPPORTS存在事务则加入,不存在就不进行事务
MANDATORY存在事务则加入,不存在就抛出异常
REQUIRES_NEW不在乎之前是否存在事务,直接开启新事务,并将之前事务挂起(存在的话)
NOT_SUPPORTED不支持事务,存在则挂起
NEVER不支持事务,存在则抛出异常
NESTED存在事务则在其中嵌套一个新的独立事务,新事务可以独立提交或回滚,若不存在事务则同REQUIRED

失效场景

访问权限

Spring事务只支持public方法,其余访问权限会导致事务失效;

//失效
@Transactional
private void add(){}

final, static修饰

Spring事务底层使用了AOP,即会需求生成代理类(jdk动态或cglib),使用上述修饰符会导致代理类无法重写,进而无法实现事务;

//失效
@Transactional
public final/static void add(){}

方法内部调用

相同类内部 a 方法调用事务方法 b 方法,不会生成事务,因为内部this调用方法无法让AOP生成代理。

//失效
@Service
public class UserService {public void a(){b();}@Transactionalpublic void b(){}
}

解决方法:

  1. 创建一个另外的Service
@Service
public class UserServiceA {public void a(){b();}
}
@Service
public class UserServiceB {@Transactionalpublic void b(){}
}
  1. 在类内注入自身
@Service
public class UserService {@Autowiredprivate UserService userService;public void a(){userService.b();}@Transactionalpublic void b(){}
}
  1. 通过AopContent类
@Service
public class UserService {public void a(){((UserService)AopContent.currentProxy()).b();}@Transactionalpublic void b(){}
}

未被Spring管理

忘记注解等操作致使Spring未管理到对象,未创建Bean实例

//失效
public class UserService {@Transactionalpublic void add(){}
}

多线程调用

Spring的事务通过数据库连接来实现,当两个方法不在相同线程中时,获取到的数据库连接不同,当前线程会各自保存不同的map(key 是数据源, value 是数据库连接)。因此一个方法中抛出异常,另外一个方法也回滚是无法做到的,只有同一个数据库连接才可以同时提交和回滚。

吞异常

开发者自己手动捕获了异常,但又没有将其抛出,意味着这个事务即使异常也不会回滚,异常被吞掉了,Spring将认为该程序是正常的。

@Service
public class UserService {    @Transactionalpublic void add() {try {a();b();} catch (Exception e) {}}
}

抛出别的异常

开发者抛出异常但其种类不正确,Spring默认只会回滚RuntimeExceptionError,对于普通Exception不会执行回滚。

@Service
public class UserService {@Transactionalpublic void add(...) throws Exception {try {...} catch (Exception e) {throw new Exception(e);}}
}

自定义回滚异常

通过事务属性rollbackFor(及其同类)自定义后,可能导致程序报错抛出异常,但非开发者自定义的异常,因此导致事务不会回滚。
即使rollbackFor有默认值,但阿里巴巴开发者规范中,还是要求开发者重新指定该参数。因为如果使用默认值,一旦程序抛出了Exception,事务不会回滚。所以,建议一般情况下,建议将该参数设置成ExceptionThrowable

嵌套事务回滚过多

当嵌套了内部事务时,若原本希望出现异常时只回滚 b 方法的内容,不回滚外部内容,但事实上全部都回滚了。因为异常会持续上抛,知道被捕获或到达最外层。

public class UserService { @Autowiredprivate RoleService roleService; @Transactionalpublic void a() {roleService.b();}
}@Service
public class RoleService { @Transactionalpublic void b() {...}
}

可将内部嵌套事务放在try...catch...中手动捕获异常,使其不继续上抛,达成只回滚内部事务,不影响外部。

public class UserService { @Autowiredprivate RoleService roleService; @Transactionalpublic void a() throws Exception {try {roleService.b();} catch (Exception) {}}
}@Service
public class RoleService { @Transactionalpublic void b() {...}
}

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Centos7在线安装mysql5.7

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 安装Mysql yum源1、卸载旧环境2、下载mysql yum源3、上传到自己服务器1&#xff09;、上传源2&#xff09;、安装yum源3&#xff09;、查看yum源是否安装成功 安装M…

(一)基于IDEA的JAVA基础15

还是先来说一下: Arrays工具类 Arrays是java.util包提供的工具类 提供了操作数组的方法&#xff0c;如排序,查询等。 如排序(升序)使用sort方法 语法: Arrays.sort(数组名)&#xff1b; 还是直接写来看看: public class Test01 { public static void main(String[] args)…

深拷贝总结

JSON.parse(JSON.stringify(obj)) 这行代码的运行过程&#xff0c;就是利用 JSON.stringify 将js对象序列化&#xff08;JSON字符串&#xff09;&#xff0c;再使用JSON.parse来反序列化&#xff08;还原&#xff09;js对象&#xff1b;序列化的作用是存储和传输。&#xff08…

InnoDB的使用限制有哪些

InnoDB的使用限制有哪些 以下是一些使用InnoDB在使用中的限制&#xff0c;包含InnoDb表&#xff0c;索引&#xff0c;表空间&#xff0c;和InnoDB存储引擎其他方面的各种限制。 一个表最多包含1017列字段&#xff0c;虚拟生成的列也包含在这个限制中。 每个表的元数据需要在…

Python求利率

要求 编写程序计算在给定利率、指定年数的情况下投资的未来值。这个计算公式如下。 使用文本域输入投资额、年份和利率。当用户单击“calculate”按钮时&#xff0c;在文本域中显示未来的投资值&#xff0c;如图所示。 代码实现 import tkinter as tkdef calculate():amou…

Vol.46 一个在线小游戏网站,每个月50万访问量

大家好&#xff0c;我是欧维Ove&#xff0c;今天要给大家分享的网站是&#xff1a;小霸王&#xff0c;这是一个可以在线玩小霸王游戏的网站&#xff0c;网址是&#xff1a;小霸王&#xff0c;其樂無窮。紅白機&#xff0c;FC線上遊戲&#xff0c;街機遊戲&#xff0c;街機線上&…

全栈的自我修养 ———— react实现滑动验证

实现滑动验证 展示依赖实现不借助create-puzzle借助create-puzzle 展示 依赖 npm install rc-slider-captcha npm install create-puzzleapi地址 实现 不借助create-puzzle 需要准备两张图片一个是核验图形&#xff0c;一个是原图------> 这个方法小编试了后感觉比较麻烦…

Transformer - 注意⼒机制 Attention 中的 Q, K, V 解释(2)

Transformer - 注意⼒机制 Attention 中的 Q, K, V 解释&#xff08;2&#xff09; flyfish Transformer - 注意⼒机制 Scaled Dot-Product Attention 计算过程 Transformer - 注意⼒机制 代码实现 Transformer - 注意⼒机制 Scaled Dot-Product Attention不同的代码比较 Tran…

CSS快速入门

目录 一、CSS介绍 1、什么是CSS&#xff1f; ​编辑2、基本语法规范 3、引入方式 4、规范 二、CSS选择器 1、标签选择器 2、类&#xff08;class&#xff09;选择器 3、id选择器 4、通配符选择器 5、复合选择器 三、常用CSS 1、color 2、font-size 3、border 4…

WPS基础使用

个人笔记&#xff08;整理不易&#xff0c;有帮助&#xff0c;收藏点赞评论&#xff0c;爱你们&#xff01;&#xff01;&#xff01;你的支持是我写作的动力&#xff09; 笔记目录&#xff1a;学习笔记目录_pytest和unittest、airtest_weixin_42717928的博客-CSDN博客 个人随笔…

前端知识学习笔记-六(vue)

简介 Vue是前端优秀框架是一套用于构建用户界面的渐进式框架 Vue优点 Vue是目前前端最火的框架之一 Vue是目前企业技术栈中要求的知识点 vue可以提升开发体验 Vue学习难度较低 Vue开发前准备 一、nodejs环境 Nodejs简介 Nodejs诞生于2009年&#xff0c;主攻服务器方向&#x…

【Spring Boot】深入解密Spring Boot日志:最佳实践与策略解析

&#x1f493; 博客主页&#xff1a;从零开始的-CodeNinja之路 ⏩ 收录文章&#xff1a;【Spring Boot】深入解密Spring Boot日志&#xff1a;最佳实践与策略解析 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 目录 Spring Boot 日志一. 日志的概念?…