[Spring] Spring事务与事务的传播

news2024/12/28 5:05:40

🌸个人主页:https://blog.csdn.net/2301_80050796?spm=1000.2115.3001.5343
🏵️热门专栏:
🧊 Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm=1001.2014.3001.5482
🍕 Collection与数据结构 (92平均质量分)https://blog.csdn.net/2301_80050796/category_12621348.html?spm=1001.2014.3001.5482
🧀线程与网络(96平均质量分) https://blog.csdn.net/2301_80050796/category_12643370.html?spm=1001.2014.3001.5482
🍭MySql数据库(93平均质量分)https://blog.csdn.net/2301_80050796/category_12629890.html?spm=1001.2014.3001.5482
🍬算法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12676091.html?spm=1001.2014.3001.5482
🍃 Spring(97平均质量分)https://blog.csdn.net/2301_80050796/category_12724152.html?spm=1001.2014.3001.5482
感谢点赞与关注~~~
在这里插入图片描述

目录

  • 1. 回忆数据库中的事务
  • 2. Spring中事务的实现
    • 2.1 Spring编程式事务
    • 2.2 Spring声明式事务@Transactional
    • 2.3 @Transactional的作用
  • 3. @Transactional详解
    • 3.1 rollbackFor
    • 3.2 事务隔离级别
      • 3.2.1 回顾MySQL中的事务隔离级别
      • 3.3.2 Spring事务隔离级别
    • 3.3 Spring事务传播机制
      • 3.3.1 概念
      • 3.3.2 事务的传播机制分类
      • 3.3.3 Spring事务传播机制代码演示

1. 回忆数据库中的事务

https://lilesily12385.blog.csdn.net/article/details/137935719

2. Spring中事务的实现

Spring中的事务操作分为两类:

  1. 编程式事务(手动写代码操作事务)
  2. 声明式事务(使用注解完成)

现有一需求: 用户注册,注册时候在日志表中插入一条操作记录
数据准备:

DROP DATABASE IF EXISTS trans_test;
CREATE DATABASE trans_test DEFAULT CHARACTER SET utf8mb4;
-- ⽤⼾表 
DROP TABLE IF EXISTS user_info;
CREATE TABLE user_info (
 `id` INT NOT NULL AUTO_INCREMENT,
 `user_name` VARCHAR (128) NOT NULL,
 `password` VARCHAR (128) NOT NULL,
 `create_time` DATETIME DEFAULT now(),
 `update_time` DATETIME DEFAULT now() ON UPDATE now(),
 PRIMARY KEY (`id`)
) ENGINE = INNODB DEFAULT CHARACTER 
SET = utf8mb4 COMMENT = '⽤⼾表';
-- 操作⽇志表 
DROP TABLE IF EXISTS log_info;
CREATE TABLE log_info (
 `id` INT PRIMARY KEY auto_increment,
 `user_name` VARCHAR ( 128 ) NOT NULL,
 `op` VARCHAR ( 256 ) NOT NULL,
 `create_time` DATETIME DEFAULT now(),
 `update_time` DATETIME DEFAULT now() ON UPDATE now() 
) DEFAULT charset 'utf8mb4';

代码准备:
1. 创建项目,配置文件引入日志打印,驼峰转换,数据库配置等.

spring:
 datasource:
 url: jdbc:mysql://127.0.0.1:3306/trans_test?
characterEncoding=utf8&useSSL=false
 username: root
 password: root
 driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
 configuration: # 配置打印 MyBatis⽇志 
 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
 map-underscore-to-camel-case: true #配置驼峰⾃动转换 

创建实体类:

import lombok.Data;
import java.util.Date;
@Data
public class UserInfo {
	private Integer id;
	private String userName;
	private String password;
	private Date createTime;
	private Date updateTime;
}
mport lombok.Data;
import java.util.Date;
@Data
public class LogInfo {
	private Integer id;
	private String userName;
	private String op;
	private Date createTime;
	private Date updateTime;
}

Mapper:

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserInfoMapper {
 @Insert("insert into user_info(`user_name`,`password`)values(#{name},#{password})")
	Integer insert(String name,String password);
}
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface LogInfoMapper {
 @Insert("insert into log_info(`user_name`,`op`)values(#{name},#{op})")
	Integer insertLog(String name,String op);
}

Service:

@Slf4j
@Service
public class UserService {
	@Autowired
	private UserInfoMapper userInfoMapper;
	public void registryUser(String name,String password){
	//插⼊⽤⼾信息 
	userInfoMapper.insert(name,password);
	}
}
 @Slf4j
 @Service
 public class LogService {
	@Autowired
	private LogInfoMapper logInfoMapper;
	public void insertLog(String name,String op){
	//记录⽤⼾操作 
	logInfoMapper.insertLog(name,"⽤⼾注册");
	}
 }

2.1 Spring编程式事务

Spring手动操作事务主要分为三步,与Sql事务的操作类似:
- 开启事务
- 提交事务
- 或者回滚事务

@RestController
@RequestMapping("/user")
public class TransactionController {
    @Autowired
    private UserService userService;
    @Autowired
    private DataSourceTransactionManager dataSourceTransactionManager;
    @Autowired
    private TransactionDefinition transactionDefinition;
    @RequestMapping("/registry")
    public String login(String userName,String password){
        //开启事务
        TransactionStatus transaction = dataSourceTransactionManager.getTransaction(transactionDefinition);
        //用户注册
        userService.registryUser(userName,password);
        //提交事务
        dataSourceTransactionManager.commit(transaction);
        //回滚事务
//        dataSourceTransactionManager.rollback(transaction);
        return "注册成功";
    }
}

SpringBoot内置了两个对象:

  1. DataSourceTransactionManager,数据源事务管理器,一般用于事务的开启回滚和提交.
  2. TransactionDefinition是事务的属性,一般用于传给事务管理器的getTransaction方法,用于事务的获取与开启.
  • 观察事务提交
dataSourceTransactionManager.commit(transaction);

在这里插入图片描述
在这里插入图片描述
我们观察到数据库,数据被插入成功.

  • 观察事务回滚
dataSourceTransactionManager.rollback(transaction);

在这里插入图片描述
在这里插入图片描述
我们看到虽然返回的结果是注册成功,但是数据库中并没有多出任何数据.
下面我们来学习一种简单而快捷的方法,使用注解声明.

2.2 Spring声明式事务@Transactional

一共有两部操作:

  1. 添加依赖
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
</dependency>
  1. 在事务的方法上添加@Transactional注解就可以实现了.无需手动开启和提交事务,在事务全部执行完成之后会自动提交,在中途发生异常的手会自动回滚.
@RestController
@RequestMapping("/user2")
public class TransactionController2 {
    @Autowired
    private UserService userService;
    @RequestMapping("/registry2")
    @Transactional
    public String registry2(String userName,String password){
        userService.registryUser(userName,password);
        return "注册成功";
    }
}

运行程序后,数据插入成功.
修改程序,使之出现异常:

@RestController
@RequestMapping("/user2")
public class TransactionController2 {
    @Autowired
    private UserService userService;
    @RequestMapping("/registry2")
    @Transactional
    public String registry2(String userName,String password){
        userService.registryUser(userName,password);
        int i = 10/0;
        return "注册成功";
    }
}

运行之后,虽然返回了注册成功,但是数据库并没有更新结果.

我们一般写事务的时候会在业务逻辑层来控制事务,因为在业务逻层中,一个业务功能可能会包含多个数据库访问操作,这样就可以把多个访问数据库的操作 合并在同一个事务中.

2.3 @Transactional的作用

@Transactional可以用来修饰方法或者是类.
修饰方法的时候,只对public修饰的方法生效,修饰其他方法也不会报错,但是也不会生效.
修饰类的时候,对类中的所有public方法生效.
在程序出现异常的时候,如果异常没有被捕获,这时候事务就会被回滚,但是如果异常被捕获,就会被认为是正常执行,依然会提交事务.
修改上述代码:

@RestController
@RequestMapping("/user2")
public class TransactionController2 {
    @Autowired
    private UserService userService;
    @RequestMapping("/registry2")
    @Transactional
    public String registry2(String userName,String password){
        userService.registryUser(userName,password);
        try {
            int i = 10/0;
        }catch (ArithmeticException e) {
            e.printStackTrace();
        }
        return "注册成功";
    }
}

运行程序之后,虽然出错了,由于异常得到了捕获,事务便得到了提交.
以下两种情况,事务依然会回滚:

  1. 重新抛出异常
try {
    int i = 10/0;
}catch (ArithmeticException e) {
    throw e;
}
  1. 手动回滚事务
    使用TransactionAspectSupport.currentTransactionStatus()得到当前事务,并使用setRollbackOnly回滚.
try {
    int i = 10/0;
}catch (ArithmeticException e) {
    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}

3. @Transactional详解

我们主要学习@Transactional注解的三个属性:
1. rollbackFor:异常回滚属性,指定能够触发事务回滚的异常类型.可以指定多个异常类型.
2. Isolation:事务的隔离级别,默认是Isolation.DEFAULT.
3. propagation:事务的传播机制.默认值为propagation.REQUIRED

3.1 rollbackFor

异常回滚的时候,@Transactional默认在遇到Error或者运行时异常时才会回滚.
在这里插入图片描述
接下来我们来使用代码验证:

@Transactional
@RequestMapping("/registry3")
public String registry3(String userName,String password) throws IOException {
    userService.registryUser(userName,password);
    if (true){
        throw new IOException();
    }
    return "注册成功";
}

向服务器提交数据:
在这里插入图片描述
在这里插入图片描述
我们看到,虽然抛出了异常,但是数据库的数据仍然被修改了.
如果我们要想指定回滚异常的类型,我们需要通过@Transactional的rollbackFor属性来完成,给属性传入异常的类对象来实现对回滚异常的指定,

@RequestMapping("/registry3")
@Transactional(rollbackFor = Exception.class)
public String registry3(String userName,String password) throws IOException {
    userService.registryUser(userName,password);
    if (true){
        throw new IOException();
    }
    return "注册成功";
}

运行程序:
在这里插入图片描述在这里插入图片描述
我们看到,事务并没有进行提交,被回滚了,数据库的数据并没有更行.

3.2 事务隔离级别

3.2.1 回顾MySQL中的事务隔离级别

https://lilesily12385.blog.csdn.net/article/details/137935719

3.3.2 Spring事务隔离级别

Spring中事务的隔离级别有5种:

  1. Isolation.DEFAULT: 以连接数据库的隔离级别为准.
  2. Isolation.READ_UNCOMMITTED:读未提交
  3. Isolating.READ_COMMITTED:读提交.
  4. Isolation.REPEATABLE_READ:可重复读.
  5. Isolation.SERIALIZABLE:串行化.
public enum Isolation {
    DEFAULT(-1),
    READ_UNCOMMITTED(1),
    READ_COMMITTED(2),
    REPEATABLE_READ(4),
    SERIALIZABLE(8);

    private final int value;

    private Isolation(int value) {
        this.value = value;
    }

    public int value() {
        return this.value;
    }
}

Spring中的隔离级别可以通过@Transactional中的Isolation属性进行设置.

@RequestMapping("/registry3")
@Transactional(isolation = Isolation.DEFAULT)
public String registry3(String userName,String password) throws IOException {
    userService.registryUser(userName,password);
    return "注册成功";
}

3.3 Spring事务传播机制

3.3.1 概念

事务的传播机制就是:多个事务方法存在调用关系的时候,事务是如何在这些方法之间进行传播的.事务的传播机制就解决的是一个事务在多个节点上(方法)中的传递.
在这里插入图片描述

比如有两个方法A和B,他们都被@Transactional修饰,方法A调用了方法B.A方法运行的时候,会开启一个新的事物,当A调用B的时候,B方法本身也有事务,此时B方法运行的时候,是假如A事务,还是创建一个新的事务呢?下面我们就来介绍一下事务的传播机制.

3.3.2 事务的传播机制分类

@Transactional注解支持事务的传播机制的设置,我们可以通过propagation属性来设置传播行为.

  1. Propagation.REQUIRED:加入事务,默认的事务传播级别.如果当前存在事务,则加入该事务,如果当前没有事务,则创建一个新的事务.(加入事务,就是共用一个事务,一个事务发生异常,全部回滚.)
  2. Propagation.SUPPORTS:如果当前存在事务,则加入该事务.如果当前没有事务,则以非事务的方式继续运行.
  3. Propagation.MANDATORY:强制性,如果当前存在事务,则加入该事务,如果当前没有事务,则抛出异常.
  4. Propagation.REQUIERS_NEW:新建事务,创建一个新事务如果当前存在事务,则把当前事务挂起.(发生异常,不影响其他事务)也就是不管外部方法是否开启事务,Propagation.REQUIERS_NEW修饰的内部方法都会新开启自己的事务,且开启的事务相互独立,互不干涉.
  5. Propagation.NOT_SUPPORTED: 以非事务的方式运行,如果当前存在事务则把当前事务挂起.
  6. Propagation.NEVER: 不支持当前事务,以非事务的方式运行,如果存在事务,则抛出异常.
  7. Propagation.NESTED: 嵌套事务,如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行,如果当前没有事务,则该取值等价于Propagation.REQUIRED.
    1,4对应,2,5对应,3,6对应.
public enum Propagation {
    REQUIRED(0),
    SUPPORTS(1),
    MANDATORY(2),
    REQUIRES_NEW(3),
    NOT_SUPPORTED(4),
    NEVER(5),
    NESTED(6);

    private final int value;

    private Propagation(int value) {
        this.value = value;
    }

    public int value() {
        return this.value;
    }
}

举例说明:一对新人结婚,需要房子

  1. Propagation.REQUIRES: 如果你有房子,就住你的房子(加入事务),如果你没有房子,我们就一起买房子(创建一个新事务).
  2. Propagation.SUPPORTS: 如果你有房子,我们就住你的房子(加入事务),如果没有房子,我们就租房子(以非事务的方式运行).
  3. Propagation.MANDATORY: 要求必须有房子(加入事务),如果没有房子,就不结婚(抛出异常)
  4. Propagation.REQUIERS_NEW: 必须买新房,不管你有没有房子,必须两个人一起买房(创建一个新事务),即使有房也不住(当前事务挂起).
  5. Propagation.NOT_SUPPORTED: 不管你有没有房子,我都不住(挂起当前事务),必须租房(以非事务的方式运行).
  6. Propagation.NEVER:不能有房子(当前存在事务),有房子就不结婚(抛出异常).
  7. Propagation.NESTED :如果你没房,就⼀起买房.如果你有房,我们就以房子为根据地,做点下生意.(如果如果当前存在事务,则创建⼀个事务作为当前事务的嵌套事务来运行.如果当前没有事务,则该取值等价于PROPAGATION_REQUIRED )

3.3.3 Spring事务传播机制代码演示

重点关注两个: REQUIRED,REQUIERS_NEW.

  • REQUIRED(加入事务)
    用户注册,插入一条数据,并记录操作日志.
@RequestMapping("/r4")
@Transactional
public String registry4(String userName,String password){
    userService.registryUser(userName,password);
    logService.insertLog(userName,"用户注册");
    return "注册成功";
}
@Service
public class UserService {
    @Autowired
    public UserInfoMapper userInfoMapper;
    @Transactional(propagation = Propagation.REQUIRED)
    public void registryUser(String name,String password) {
        userInfoMapper.insert(name, password);
    }
}
@Service
public class LogService {
    @Autowired
    private LogInfoMapper logInfoMapper;
    @Transactional(propagation = Propagation.REQUIRED)
    public void insertLog(String name,String op){
        int i = 10/0;
        //记录⽤⼾操作
        logInfoMapper.insertLog(name,"⽤户注册");
    }
}

我们在执行之后,发现数据库中并没有插入任何数据,这就是因为insertLog方法发生了异常,事务发生回滚,当事务回滚之后,registry4方法也发生了回滚,导致了registryUser也发生了回滚,导致数据库中没有插入数据.

  • REQUIRES_NEW(新建事务)
    将上面的UserService和LogService的事务传播机制改为Propagation.REQUIRES_NEW.
@RequestMapping("/r4")
@Transactional
public String registry4(String userName,String password){
    userService.registryUser(userName,password);
    logService.insertLog(userName,"用户注册");
    return "注册成功";
}
@Service
public class LogService {
    @Autowired
    private LogInfoMapper logInfoMapper;
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void insertLog(String name,String op){
        int i = 10/0;
        //记录⽤⼾操作
        logInfoMapper.insertLog(name,"⽤户注册");
    }
}
@Service
public class UserService {
    @Autowired
    public UserInfoMapper userInfoMapper;
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void registryUser(String name,String password) {
        userInfoMapper.insert(name, password);
    }
}

在执行之后,我们会发现,日志表中并没有插入新的数据,但是用户表中插入了新的数据,这是由于UserService,LogServiceregistry4属于不同的事务,LogService出现异常回滚之后不会影响registry4UserService的执行.

  • NEVER(不支持当前事务)
    REQUIRED代码的UserService中的对应方法的事务传播机制修改为Propagation.NEVER.并去掉制造的异常.
@Transactional(propagation = Propagation.NEVER)
public void insertLog(String name,String op){
    //记录⽤⼾操作
    logInfoMapper.insertLog(name,"用户注册");
}

运行之后,程序抛出异常,日志表和用户表均没有数据插入.

  • NESTED(嵌套事务)
    将上述REQUIREDUserService中的对应方法的事务传播机制修改为Propagation.NESTED.
@Transactional(propagation = Propagation.NESTED)
public void insertLog(String name,String op){
    int i = 10/0;
    //记录用户操作
    logInfoMapper.insertLog(name,"用户注册");
}
@Transactional(propagation = Propagation.NESTED)`在这里插入代码片`
public void registryUser(String name,String password) {
    userInfoMapper.insert(name, password);
}

运行程序之后,两张表中都没有插入任何数据,如果我们对出现异常的方法进行手动回滚:

@Transactional(propagation = Propagation.NESTED)
public void insertLog(String name,String op){
    try {
        int i = 10/0;
    }catch (Exception e){
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }
    //记录用户操作
    logInfoMapper.insertLog(name,"用户注册");
}

在这里插入图片描述
我们看到,用户表插入成功了,但是日志表的数据被回滚了.
在这里插入图片描述
在这里插入图片描述

区分REQUIRED和NESTED
REQUIRED:当其中一个事务出现异常的时候,所有事务都会回滚,如果try-catch语句中对事物进行手动回滚,则子事务和父事务全部会被回滚.
NESTED : 当子事务出现异常的时候,子事务对应的父事务也会回滚,但是如果在有异常的子事务中进行try-catch,catch中对事务进行手动回滚,则只有出现异常的事务被回滚,但是另一个没有出现异常的子事务没有被回滚,与REQUIRED最大的区别就是,NESTED可以做到部分回滚,但是REQUIRED只能做到全部回滚.

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

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

相关文章

借助 Aspose.Words,在 Word 文档中创建表格

Word 文档中的表格是一种强大的工具&#xff0c;可用于以清晰、结构化的格式组织和呈现数据。表格由行和列组成&#xff0c;行和列相交形成可包含文本、数字、图像或其他元素的单元格。在本文中&#xff0c;我们将学习如何使用 C# 以编程方式在 Word 文档中创建表格。本文通过代…

虚幻5|高级运动实现基础的翻滚Roll 基础篇

一&#xff0c;调整项目设置——输入 1.我设置了翻滚是同时按W&#xff0b;Shift按键&#xff0c;如果你有更好的按键安排&#xff0c;评论区留言 二&#xff0c;打开角色蓝图&#xff0c;打开图表—基础移动&#xff08;你的放图表就行了&#xff0c;我这里是安排了很多排版的…

LDRA Testbed(TBrun)软件单元测试_实例讲解(局部静态变量)

系列文章目录 LDRA Testbed软件静态分析_操作指南 LDRA Testbed软件静态分析_自动提取静态分析数据生成文档 LDRA Testbed软件静态分析_Jenkins持续集成&#xff08;自动静态分析并用邮件自动发送分析结果&#xff09; LDRA Testbed软件静态分析_软件质量度量 LDRA Testbed软件…

使用Hugging Face构建大型语言模型应用

在本文中&#xff0c;我们将介绍如何使用Hugging Face的大型语言模型&#xff08;LLM&#xff09;构建一些常见的应用&#xff0c;包括摘要&#xff08;Summarization&#xff09;、情感分析&#xff08;Sentiment analysis&#xff09;、翻译&#xff08;Translation&#xff…

初步融合snowboy+pyttsx3+espeak+sherpa-ncnn的python代码

在前文《将Snowboy语音唤醒的“叮”一声改成自定义语言》中&#xff0c;我已经实现唤醒snowboy后&#xff0c;树莓派会说一句自定义文本。今天&#xff0c;会在此基础上增加ASR的应用&#xff08;基于sherpa-ncnn&#xff09;。 首先&#xff0c;编写一个asr.py的程序&#xf…

手撕快排——三种实现方法(附动图及源码)

&#x1f916;&#x1f4bb;&#x1f468;‍&#x1f4bb;&#x1f469;‍&#x1f4bb;&#x1f31f;&#x1f680; &#x1f916;&#x1f31f; 欢迎降临张有志的未来科技实验室&#x1f916;&#x1f31f; 专栏&#xff1a;数据结构 &#x1f468;‍&#x1f4bb;&…

【C++】STL——list

前言 本篇博客我们接着来理解一个STL库里的list链表的结构&#xff0c;根据前面数据结构的铺垫&#xff0c;理解这个结构相对比较容易。我们来一起看看吧 &#x1f493; 个人主页&#xff1a;小张同学zkf ⏩ 文章专栏&#xff1a;C 若有问题 评论区见&#x1f4dd; &#x1f38…

中国与中南半岛国家多国语言系统开发i18n配置老挝、柬埔寨语言配置

前言 当下中国与中南半岛国家经济合作密切&#xff0c;同时也需要软件系统&#xff0c;多国使用系统需要实现多语言&#xff0c;我们团队最近也接到一个中、老、柬三国的业务软件&#xff0c;需要将软件做成三个国家语言。然后我们网上收i18n的老、柬的语言包命名&#xff0c;…

计算机毕业设计 美妆神域网站 美妆商城系统 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

j2:基于pytorch的resnet实验:鸟类分类

基于pytorch的resnet实验&#xff1a;鸟类分类 &#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 Ⅰ Ⅰ Ⅰ Introduction&#xff1a; 本文为机器学习使用resnet实现鸟类图片分类的实验&#xff0c;素材来自网…

跟李沐学AI:目标检测的常用算法

区域神经网络R-CNN 使用启发式搜索算法来选择锚框 -> 使用预训练模型来对每个锚框抽取特征 -> 训练一个SVM对类别进行分类 -> 训练一个线性回归模型来预测边缘框偏移 锚框大小不一&#xff0c;如何将不同的锚框统一为一个batch? -> 兴趣区域池化层 兴趣区域(RoI…

界面优化 - QSS

目录 1、背景介绍 2、基本语法 3、QSS 设置方式 3.1 指定控件样式设置 代码示例: 子元素受到影响 3.2 全局样式设置 代码示例: 使用全局样式 代码示例: 样式的层叠特性 代码示例: 样式的优先级 3.3 从文件加载样式表 代码示例: 从文件加载全局样式 3.4 使用 Qt Desi…

最新UI六零导航系统源码 | 多模版全开源

六零导航页 (LyLme Spage) 致力于简洁高效无广告的上网导航和搜索入口&#xff0c;支持后台添加链接、自定义搜索引擎&#xff0c;沉淀最具价值链接&#xff0c;全站无商业推广&#xff0c;简约而不简单。 使用PHPMySql&#xff0c;增加后台管理 多模板选择&#xff0c;支持在…

MySQL基础练习题46-每位经理的下属员工数量

目录 题目 准备数据 分析数据 总结 题目 我们将至少有一个其他员工需要向他汇报的员工&#xff0c;视为一个经理。 返回需要听取汇报的所有经理的 ID、名称、直接向该经理汇报的员工人数&#xff0c;以及这些员工的平均年龄&#xff0c;其中该平均年龄需要四舍五入到最接近…

【网络】IP分片与路径MTU发现

目录 MTU值 IP分片与重组 路径MTU发现 路径MTU发现原理 个人主页&#xff1a;东洛的克莱斯韦克-CSDN博客 相关文章&#xff1a;【网络】从零认识IPv4-CSDN博客 MTU值 由于物理层的硬件限制&#xff0c;为了使网络性能最优&#xff0c;在数据链路层会有一个MTU值&#xff0…

算法【Java】—— 双指针算法

双指针算法 常见的双指针有对撞指针&#xff0c;快慢指针以及前后指针&#xff08;这个前后指针是指两个指针都是从从一个方向出发&#xff0c;去往另一个方法&#xff0c;也可以认为是小学学习过的两车并行&#xff0c;我也会叫做同向指针&#xff09;&#xff0c;在前后指针…

Python3网络爬虫开发实战(10)模拟登录(需补充账号池的构建)

文章目录 一、基于 Cookie 的模拟登录二、基于 JWT 模拟登入三、账号池四、基于 Cookie 模拟登录爬取实战五、基于JWT 的模拟登录爬取实战六、构建账号池 很多情况下&#xff0c;网站的一些数据需要登录才能查看&#xff0c;如果需要爬取这部分的数据&#xff0c;就需要实现模拟…

KNN图像识别实例--手写数字识别

目录 前言 一、导入库 二、导入图像并处理 1.导入图像 2.提取出图像中的数字 3.将列表转换成数组 4.获取特征数据集 5.获取标签数据 三、使用KNN模型 1.创建KNN模型并训练 2.KNN模型出厂前测试 3.使用测试集对KNN模型进行测试 四、传入单个图像&#xff0c;使用该模…

叉车高位盲区显示器 无线摄像头免打孔 视线遮挡的解决方案

叉车作业货叉叉货时&#xff0c;货叉升降无法看清位置&#xff0c;特别是仓储的堆高车&#xff0c;司机把头探出去才勉强可以靠经验找准方位&#xff01;一个不小心就可能叉歪了&#xff0c;使货物倾斜、跌落等等&#xff0c;从而发生事故&#xff01;如何将隐患扼杀&#xff0…

【JAVA入门】Day21 - 时间类

【JAVA入门】Day21 - 时间类 文章目录 【JAVA入门】Day21 - 时间类一、JDK7前的时间相关类1.1 Date1.2 SimpleDateFormat1.3 Calendar 二、JDK8新增的时间相关类2.1 Date 相关类2.1.1 ZoneId 时区2.1.2 Instant 时间戳2.1.3 ZoneDateTime 带时区的时间 2.2 DateTimeFormat 相关…