【Spring 事务和事务传播机制】
目录
1 事务概述
1.1 为什么需要事务
1.2 事务的特性
1.3 Spring 中事务的实现
2 Spring 声明式事务
2.1 @Transactional
2.2 @Transactional 的作用范围
2.3 @Transactional 的各种参数
2.3.1 ioslation
2.4 事务发生了异常,也不回滚的情况
异常被捕获时
3 事务的传播机制
3.1 定义
3.2 为什么需要事务传播机制
3.3 事务传播机制种类
Propagation.REQUIRED
Propagation.SUPPORTS
Propagation.MANDATORY
Propagation.REQUIRES_NEW
Propagation.NOT_SUPPORTED
Propagation.NEVER
Propagation.NESTED
1 事务概述
1.1 为什么需要事务
事务定义
将⼀组操作封装成⼀个执⾏单元(封装到⼀起),要么全部成功,要么全部失败。
对于转账来说:
第一步操作:A 账户 -1000 元
第二步操作:B 账户 +1000 元
使用了事务之后,这一组操作要么一起执行成功,要么一起失败。如果没有事务的情况下,有可能第一步成功了,第二步失败了,那么对于 A 来说,它的账户的 1000 块钱就人间蒸发了。
1.2 事务的特性
事务有4 ⼤特性(ACID),原⼦性、持久性、⼀致性和隔离性,具体概念如下:
原⼦性(Atomicity):⼀个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中 间某个环节。事务在执⾏过程中发⽣错误,会被回滚(Rollback)到事务开始前的状态,就像这个 事务从来没有执⾏过⼀样。
⼀致性(Consistency):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写⼊的资料必须完 全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以⾃发性地完成预定的⼯ 作。
隔离性(Isolation):数据库允许多个并发事务同时对其数据进⾏读写和修改的能⼒,隔离性可以防⽌多个事务 并发执⾏时由于交叉执⾏⽽导致数据的不⼀致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串⾏化 (Serializable)。
持久性(Durability):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
1.3 Spring 中事务的实现
Spring 中事务操作分为两类:
1. 编程式事务(手动写代码操作事务)
2. 声明式事务(利用注解自动开启和提交事务)
2 Spring 声明式事务
MySQL 中,事务有三个重要的操作:开启事务、提交事务、回滚事务,对应的操作命令如下:
-- 开启事务
start transaction;
-- 业务执行
-- 提交事务
commit;
-- 回滚事务
rollback;
而 Spring 声明式事务的实现与 MySQL 事务的实现基本一致,只是前者自动的,只需要在需要的⽅法上添加 @Transactional 注解就可以实现了,⽆需⼿动开启事务和提交事务,进⼊⽅法时⾃动开启事务,⽅法执⾏完会⾃动提交事务,如果中途发⽣了没有处理的异常会⾃动回滚事务。
2.1 @Transactional
package com.example.demo.model;import lombok.Data;import java.time.LocalDateTime;@Data
public class UserInfo {private int id;private String username;private String password;private String photo;private LocalDateTime createtime;private LocalDateTime updatetime;private int state;
}
package com.example.demo.mapper;import com.example.demo.model.UserInfo;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;@Mapper
public interface UserMapper {@Insert("insert into userinfo(username,password) values(#{username},#{password})")int add(UserInfo userInfo);
}
package com.example.demo.service;import com.example.demo.mapper.UserMapper;
import com.example.demo.model.UserInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class UserService {@Autowiredprivate UserMapper userMapper;public int add(UserInfo userInfo){return userMapper.add(userInfo);}
}
package com.example.demo.controller;import com.example.demo.model.UserInfo;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RequestMapping("/user")
@RestController
public class UserController {@Autowiredprivate UserService userService;@RequestMapping("/add")@Transactionalpublic int add(){// 1. 非空判断UserInfo userInfo = new UserInfo();userInfo.setUsername("莉丝");userInfo.setPassword("6363");// 2. 调用 service 执行添加int result = userService.add(userInfo);System.out.println("result:"+result);int num = 10/0;// 3. 将结果给前端return 0;}
}
启动项目发现,使用了 @Transactional 注解的确回滚了事务:
可以看到,信息并没有添加到数据库的 userinfo 这表里。
2.2 @Transactional 的作用范围
@Transactional 可以用来修饰方法或类,修饰方法时,只能应用到 public 方法上,其他访问权限均不生效;修饰类时,该注解对该类中所有的 public 方法都生效。
2.3 @Transactional 的各种参数
参数 | 作用 |
value | 配置多个事务管理器时,使用该属性指定一个事务管理器 |
transactionManager | |
propagation | 事务的传播行为,默认值为 Propagation.REQUIRED |
isolation | 事务的隔离级别,默认值为 Isolation.DEFAULT |
timeout | 事务的超出时间,默认值为 -1。如果超出该时间限制但事务还没有完成,则自动回滚事务 |
readOnly | 是否为只读事务,默认值为 false; |
rollbackFor | 指定触发事务回滚的异常类型,可以多个 |
rollbackForClassName | 指定触发事务回滚的异常类名称,可以多个 |
noRollbackFor | 指定不回滚事务的异常类型,可以多个 |
noRollbackForClassName | 指定不回滚事务的异常类名称,可以多个 |
2.3.1 ioslation
根据文章开头的介绍,我们知道事务有四大特性,分别是原子性、一致性、隔离性以及持久性。而这四大特性中,只有隔离性,也就是隔离级别是可以设置的。
事务的隔离级别是用来保障多个并发事务执行更加可控,更符合程序员的预期。
在 Spring 中,可以通过 @Transactional 中的 isolation 属性进行设置。
MySQL 事务隔离级别:
1. READ UNCOMMITTED:读未提交,该隔离级别的事务可以看到其他事务中未提交的数据,⽽未提交的数据可能会发⽣回滚,因此我们把该级别读取到的数据称之为脏数据,把这个问题称之为脏读。
2. READ COMMITTED:读已提交,该隔离级别的事务能读取到已经提交事务的数据, 因此它不会有脏读问题。但由于在事务的执⾏中可以读取到其他事务提交的结果,所以在不同时间的相同 SQL 查询中,可能会得到不同的结果,这种现象叫做不可重复读。
3. REPEATABLE READ:可重复读,是 MySQL 的默认事务隔离级别,它能确保同⼀事务多次查询的结果⼀致,这就意味着,该级别的事务 A 正在执行,而另一个事务 B 成功插入了一条数据,但由于可重复读要保证每次查询的结果一致,所以 A 就会查询不到这条数据,但是 A 也想插入这条数据,却发现插入不了,这就是幻读。
4. SERIALIZABLE:序列化,事务最⾼隔离级别,它会强制事务排序,使之不会发⽣冲突,从⽽解决了脏读、不可重复读和幻读问题,但因为执⾏效率低,所以真正使⽤的场景并不多。
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
读未提交 | ✔ | ✔ | ✔ |
读已提交 | × | ✔ | ✔ |
可重复读 | × | × | ✔ |
串行化 | × | × | × |
● 脏读:⼀个事务读取到了另⼀个事务修改的数据之后,后⼀个事务⼜进⾏了回滚操作,从⽽导致第⼀个事务读取的数据是错误的。
● 不可重复读:⼀个事务两次查询得到的结果不同,因为在两次查询中间,有另⼀个事务把数据修改了。
● 幻读:⼀个事务两次查询中得到的结果集不同,因为在两次查询中另⼀个事务有新增了⼀部分数据。
不同的数据库的隔离级别是不一样的,MySQL 有四种,Oracle 有三种。Spring 的隔离级别为五种,是为了兼容不同的数据库。
Spring 中事务隔离级别包含以下 5 种:
1. Isolation.DEFAULT:以连接的数据库的事务隔离级别为主。
2. Isolation.READ_UNCOMMITTED:读未提交,可以读取到未提交的事务,存在脏读。
3. Isolation.READ_COMMITTED:读已提交,只能读取到已经提交的事务,解决了脏读,存在不可重复读。
4. Isolation.REPEATABLE_READ:可重复读,解决了不可重复读,但存在幻读(MySQL默认级别)。
5. Isolation.SERIALIZABLE:串⾏化,可以解决所有并发问题,但性能太低。
如果 Spring 的隔离级别与 MySQL 设置的隔离级别不同的时候,以 Spring 设置的隔离级别为主。
Spring 相当于一个客户端,设置了,以 Spring 设置的为主,没有设置时,跟连接的数据库的隔离级别一样,也就是 DEFAULT。
隔离级别枚举的值以 2 的次方,性能更高。
2.4 事务发生了异常,也不回滚的情况
异常被捕获时
package com.example.demo.controller;import com.example.demo.model.UserInfo;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RequestMapping("/user")
@RestController
public class UserController {@Autowiredprivate UserService userService;@RequestMapping("/add")@Transactionalpublic int add(){// 1. 非空判断UserInfo userInfo = new UserInfo();userInfo.setUsername("莉丝");userInfo.setPassword("6363");// 2. 调用 service 执行添加int result = userService.add(userInfo);System.out.println("result:"+result);try{int num = 10/0;}catch (Exception e){}// 3. 将结果给前端return 0;}
}
@Transactional 是AOP,出现异常的时候,代理对象可以感知到异常,并进行事务的回滚。但如果异常被捕获了,外部的代理对象就感知不到了,就不会进行事务的回滚了。
对此有两种解决方案。
1. 将异常继续抛出去,让代理对象感知到异常,也就能自动的回滚事务了。
package com.example.demo.controller;import com.example.demo.model.UserInfo;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RequestMapping("/user")
@RestController
public class UserController {@Autowiredprivate UserService userService;@RequestMapping("/add")@Transactionalpublic int add(){// 1. 非空判断UserInfo userInfo = new UserInfo();userInfo.setUsername("埃罗尔");userInfo.setPassword("9146");// 2. 调用 service 执行添加int result = userService.add(userInfo);System.out.println("result:"+result);try{int num = 10/0;}catch (Exception e){throw e;}// 3. 将结果给前端return 0;}
}
2. 使用代码,手动回滚事务
package com.example.demo.controller;import com.example.demo.model.UserInfo;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RequestMapping("/user")
@RestController
public class UserController {@Autowiredprivate UserService userService;@RequestMapping("/add")@Transactionalpublic int add(){// 1. 非空判断UserInfo userInfo = new UserInfo();userInfo.setUsername("埃罗尔");userInfo.setPassword("9146");// 2. 调用 service 执行添加int result = userService.add(userInfo);System.out.println("result:"+result);try{int num = 10/0;}catch (Exception e){TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}// 3. 将结果给前端return 0;}
}
在网页没有报错的情况下,进行了事务的回滚。
3 事务的传播机制
3.1 定义
Spring 事务传播机制定义了多个包含了事务的⽅法,相互调⽤时,事务是如何在这些⽅法间进⾏传递的。
3.2 为什么需要事务传播机制
事务隔离级别是保证多个并发事务执⾏的可控性的(稳定性的),⽽事务传播机制是保证⼀个事务在多个调⽤⽅法间的可控性的(稳定性的)。
8
3.3 事务传播机制种类
Propagation.REQUIRED
默认的事务传播级别,如果当前存在事务,就加入这个事务(加入意味着成为这个外部事务的一部分),如果不存在事务,则会自己创建一个新的事务。与其他被调用的事务,要么一起提交事务,要么一起回滚事务。
package com.example.demo.controller;import com.example.demo.model.UserInfo;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RequestMapping("/user")
@RestController
public class UserController {@Autowiredprivate UserService userService;@RequestMapping("/add")@Transactionalpublic int add(){// 1. 非空判断UserInfo userInfo = new UserInfo();userInfo.setUsername("埃罗尔");userInfo.setPassword("9146");// 2. 调用 service 执行添加int result = userService.add(userInfo);System.out.println("result:"+result);try{int num = 10/0;}catch (Exception e){TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}// 3. 将结果给前端return 0;}
}
package com.example.demo.service;import com.example.demo.mapper.UserMapper;
import com.example.demo.model.UserInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;@Service
public class UserService {@Autowiredprivate UserMapper userMapper;@Transactionalpublic int add(UserInfo userInfo){int result = userMapper.add(userInfo);System.out.println("add result -> " + result);insert(userInfo);return result;}@Transactionalpublic int insert(UserInfo userInfo){int result = userMapper.add(userInfo);System.out.println("insert result -> " + result);int num = 10 / 0;return result;}
}
add 调用两个加了@Transactional 的 service 的方法。
Propagation.SUPPORTS
如果当前存在事务(存在于别的方法开启的事务中,而不是自己启动事务),则加入该事务;如果没有,则以非事务的方式继续运行。
修改 insert 方法如下,添加 propagation 参数:
package com.example.demo.controller;import com.example.demo.model.UserInfo;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RequestMapping("/user")
@RestController
public class UserController {@Autowiredprivate UserService userService;@RequestMapping("/add")@Transactional(propagation = Propagation.SUPPORTS)public int add(){// 1. 非空判断UserInfo userInfo = new UserInfo();userInfo.setUsername("埃罗尔");userInfo.setPassword("9146");// 2. 调用 service 执行添加int result = userService.add(userInfo);System.out.println("result:"+result);try{int num = 10/0;}catch (Exception e){
// throw e;TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}// 3. 将结果给前端return 0;}
}
对于上述 add 来说,它并没有存在于任何一个事务当中,所以它按照非事务的方式运行。
package com.example.demo.service;import com.example.demo.mapper.UserMapper;
import com.example.demo.model.UserInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;@Service
public class UserService {@Autowiredprivate UserMapper userMapper;@Transactional(propagation = Propagation.SUPPORTS)public int add(UserInfo userInfo){int result = userMapper.add(userInfo);System.out.println("add result -> " + result);insert(userInfo);return result;}@Transactional(propagation = Propagation.SUPPORTS)public int insert(UserInfo userInfo){int result = userMapper.add(userInfo);System.out.println("insert result -> " + result);int num = 10 / 0;return result;}}
service 中, add 被 controller 调用,由于 controller 不存在事务,因此 add 也不存在于任何事务中,所以 add 按照非事务的方式运行,同样对于 insert 来说也是如此,按照非事务的方式运行。
运行结果是:添加了两条信息:
如果 controller 的 add 的事务传播机制改成 Propagation.REQUIRED ,其他两个方法不变,结果会怎么样呢?
package com.example.demo.controller;import com.example.demo.model.UserInfo;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RequestMapping("/user")
@RestController
public class UserController {@Autowiredprivate UserService userService;@RequestMapping("/add")@Transactional(propagation = Propagation.REQUIRED)public int add(){// 1. 非空判断UserInfo userInfo = new UserInfo();userInfo.setUsername("埃罗尔");userInfo.setPassword("9146");// 2. 调用 service 执行添加int result = userService.add(userInfo);System.out.println("result:"+result);try{int num = 10/0;}catch (Exception e){
// throw e;TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}// 3. 将结果给前端return 0;}
}
Propagation.MANDATORY
mandatory 强制性的意思。如果当前存在事务,则加入该事务;如果没有事务,则抛出异常。
Propagation.REQUIRES_NEW
表示创建⼀个新的事务,如果当前存在事务,则把当前事务挂 起。也就是说不管外部⽅法是否开启事务,Propagation.REQUIRES_NEW 修饰的内部⽅法会新开 启⾃⼰的事务,且开启的事务相互独⽴,互不⼲扰。
Propagation.NOT_SUPPORTED
以⾮事务⽅式运⾏,如果当前存在事务,则把当前事务挂起。
Propagation.NEVER
以⾮事务⽅式运⾏,如果当前存在事务,则抛出异常。
package com.example.demo.controller;import com.example.demo.model.UserInfo;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RequestMapping("/user")
@RestController
public class UserController {@Autowiredprivate UserService userService;@RequestMapping("/add")@Transactional(propagation = Propagation.REQUIRED)public int add(){// 1. 非空判断UserInfo userInfo = new UserInfo();userInfo.setUsername("埃罗尔");userInfo.setPassword("9146");// 2. 调用 service 执行添加int result = userService.add(userInfo);System.out.println("result:"+result);try{int num = 10/0;}catch (Exception e){
// throw e;TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}// 3. 将结果给前端return 0;}
}
package com.example.demo.service;import com.example.demo.mapper.UserMapper;
import com.example.demo.model.UserInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;@Service
public class UserService {@Autowiredprivate UserMapper userMapper;@Transactional(propagation = Propagation.NEVER)public int add(UserInfo userInfo){int result = userMapper.add(userInfo);System.out.println("add result -> " + result);insert(userInfo);return result;}@Transactional(propagation = Propagation.NEVER)public int insert(UserInfo userInfo){int result = userMapper.add(userInfo);System.out.println("insert result -> " + result);int num = 10 / 0;return result;}}
运行结果:
不添加任何一条信息。
Propagation.NESTED
如果当前存在事务,则创建⼀个事务作为当前事务的嵌套事务来运⾏;如果当前没有事务,则该取值等价于 PROPAGATION_REQUIRED。
嵌套的事务不影响外部的事务。NESTED 嵌套 NESTED ,一个完蛋,所有的 NESTED 都完蛋。
package com.example.demo.controller;import com.example.demo.model.UserInfo;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RequestMapping("/user")
@RestController
public class UserController {@Autowiredprivate UserService userService;@RequestMapping("/add")@Transactional(propagation = Propagation.REQUIRED)public int add(){// 1. 非空判断UserInfo userInfo = new UserInfo();userInfo.setUsername("玛丽莲·梦露");userInfo.setPassword("18");// 2. 调用 service 执行添加int result = userService.add(userInfo);System.out.println("result:"+result);userService.insert(userInfo);// 3. 将结果给前端return 0;}
}
package com.example.demo.service;import com.example.demo.mapper.UserMapper;
import com.example.demo.model.UserInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;@Service
public class UserService {@Autowiredprivate UserMapper userMapper;@Transactional(propagation = Propagation.NESTED)public int add(UserInfo userInfo){int result = userMapper.add(userInfo);System.out.println("add result -> " + result);return result;}@Transactional(propagation = Propagation.NESTED)public int insert(UserInfo userInfo){int result = userMapper.add(userInfo);System.out.println("insert result -> " + result);try {int num = 10 / 0;}catch (Exception e){TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}return result;}
}
如果 insert 方法写成以下这种形式,不但 insert 感知到了异常,调用方感知到了异常,就会造成总体回滚,这就意味着数据无法添加。
@Transactional(propagation = Propagation.NESTED)public int insert(UserInfo userInfo){int result = userMapper.add(userInfo);System.out.println("insert result -> " + result);int num = 10 / 0;return result;}
而如果对 insert 进行手动回滚操作,这样只有 insert 感知到了异常,但是调用方并没有感知。
为什么把 insert 放在 add 里,也会回滚。
相关文章:

【Spring 事务和事务传播机制】
目录 1 事务概述 1.1 为什么需要事务 1.2 事务的特性 1.3 Spring 中事务的实现 2 Spring 声明式事务 2.1 Transactional 2.2 Transactional 的作用范围 2.3 Transactional 的各种参数 2.3.1 ioslation 2.4 事务发生了异常,也不回滚的情况 异常被捕获时 3 事务的传…...

【爬虫】实验项目二:模拟登录和数据持久化
目录 一、实验目的 二、实验预习提示 三、实验内容 实验要求 基本要求: 改进要求A: 改进要求B: 四、实验过程 基本要求: 源码如下: 改进要求A: 源码如下: 改进要求B: 源码如下&…...

图文版:以太网二层接口类型(含配套习题)
常见的以太网二层接口类型包括以下三种: 一、Access接口 access链路类型端口,一种交换机的主干道模式,2台交换机的2个端口之间是否能够建立干道连接,取决于这2个端口模式的组合。 Access端口在收到以太网帧后打开VLAN标签&#…...

生信豆芽菜-机器学习筛选特征基因
网址:http://www.sxdyc.com/mlscreenfeature 一、使用方法 1、准备数据 第一个文件:特征表达数据 第二个文件:分组信息,第一列为样本名,第二列为患者分组 第三个文件:分析基因名 2、选择机器学习的方…...

v-html富文本里面的图片设置宽高不起作用的原因
把scoped去掉...

pdf文档怎么压缩小一点?文件方法在这里
在日常工作和生活中,我们经常会遇到需要上传或者发送pdf文档的情况。但是,有时候pdf文档的大小超出了限制,需要我们对其进行压缩。那么,如何将pdf文档压缩得更小一点呢?下面,我将介绍三种方法,让…...

CMD关闭占用端口
1. netstat -ano | findstr :xxxx 2. taskkill /pid xxxx 3. 强制关闭taskkill/F /pid xxxx...

复制粘贴是怎么实现的
在上面的代码中,command 和 select 是自定义的函数。它们的作用如下: 实现复制粘贴的思路: 创建一个 textarea 标签将 textarea 移出可视区域给这个 textarea 赋值将这个 textarea 标签添加到页面中调用 textarea 的 select 方法调用 docum…...

mybatisplus多租户原理略解
概述 当前mybatisPlus版本 <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.3.2</version> </dependency>jdk版本:17 springboot版本:…...

Spring整合RabbitMQ-配制文件方式-1-消息生产者
Spring-amqp是对AMQP的一些概念的一些抽象,Spring-rabbit是对RabbitMQ操作的封装实现。 主要有几个核心类RabbitAdmin、RabbitTemplate、SimpleMessageListenerContainer等 RabbitAdmin类完成对Exchange、Queue、Binding的操作,在容器中管理 了RabbitA…...

Python Opencv实践 - 凸包检测(ConvexHull)
import cv2 as cv import numpy as np import matplotlib.pyplot as pltimg cv.imread("../SampleImages/stars.png") plt.imshow(img[:,:,::-1])img_contour img.copy() #得到灰度图做Canny边缘检测 img_gray cv.cvtColor(img_contour, cv.COLOR_BGR2GRAY) edges…...

IP网络广播系统有哪些优点
IP网络广播系统有哪些优点 IP网络广播系统有哪些优点? IP网络广播系统是基于 TCP/IP 协议的公共广播系统,采用 IP 局域网或 广域网作为数据传输平台,扩展了公共广播系统的应用范围。随着局域网络和 网络的发展 , 使网络广播的普及变为可能 …...

【LeetCode】83. 删除排序链表中的重复元素
83. 删除排序链表中的重复元素(简单) 方法:一次遍历 思路 由于给定的链表是排好序的,因此重复的元素在链表中出现的位置是连续的,因此我们只需要对链表进行一次遍历,就可以删除重复的元素。 从指针 cur 指…...

【大数据】Flink 详解(七):源码篇 Ⅱ
本系列包含: 【大数据】Flink 详解(一):基础篇【大数据】Flink 详解(二):核心篇 Ⅰ【大数据】Flink 详解(三):核心篇 Ⅱ【大数据】Flink 详解(四…...

stable diffusion实践操作-SD原理
系列文章目录 本文专门开一节写SD原理相关的内容,在看之前,可以同步关注: stable diffusion实践操作 文章目录 系列文章目录前言一、原理说明1.1、出图原理1.1.1 AI画画不是和人一样,从0开始,而是一个去噪点的过程&am…...

C++ Primer Plus第十三章编程练习答案
1,以下面的类声明为基础: // base class class Cd{ // represents a CD disk private: char performers[50] ; char label[20]; int selections;// number of selections double playtime; // playing time in minutes public: Cd(char * sl,char * s2,int n,double…...

Elasticsearch:wildcard - 通配符搜索
Elasticsearch 是一个分布式、免费和开放的搜索和分析引擎,适用于所有类型的数据,例如文本、数字、地理空间、结构化和非结构化数据。 它基于 Apache Lucene 构建,Apache Lucene 是一个全文搜索引擎,可用于各种编程语言。 由于其速…...

配置类安全问题学习小结
目录 一、前言 二、漏洞类型 目录 一、前言 二、漏洞类型 2.1 Strict Transport Security Not Enforced 2.2 SSL Certificate Cannot Be Trusted 2.3 SSL Anonymous Cipher Suites Supported 2.4 "Referrer Policy”Security 头值不安全 2.5 “Content-Security-…...

IMX6ULL移植篇-uboot源码目录
一. uboot 源码分析前提 由于 uboot 会使用到一些经过编译才会生成的文件,因此,我们在分析 uboot的时候,需要先编译一下 uboot 源码工程。 这里所用的开发板是 nand-flash版本。 二. uboot 源码目录及编译 1. uboot 源码目录 uboot源码目…...

SAP MM学习笔记27- 购买依赖(采购申请)
前面已经努力的学习了 购买发注,入库,请求书照合 等功能,还是蛮多内容的哈。 剩下的功能,比如 右侧的 所要量决定,供给元决定,仕入先选择 还没学。 从这章开始,要开始学习它们了。 这一章先来…...

C++零碎记录(八)
14. 运算符重载简介 14.1 运算符重载简介 ① 运算符重载:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型。 ② 对于内置的数据类型的表达式的运算符是不可能改变的。 14.2 加号运算符重载 ① 加号运算符作用&#x…...

基于matlab的扩频解扩误码率完整程序分享
clc; clear; close all; warning off; addpath(genpath(pwd)); r5; N2^r-1;%周期31 aones(1,r); mzeros(1,N); for i1:(2^r-1) temp mod((a(5)a(2)),2); for jr:-1:2 a(j)a(j-1); end a(1)temp; m(i)a(r); end mm*2-1;%双极性码 %产生随…...

算法:轮转数组---循环取模运算
1、题目: 给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。 2、分析特点: 轮转 > 取模运算 我们可以使用额外的数组来将每个元素放至正确的位置。用 n 表示数组的长度,我们遍历原数组&a…...

Vue教程
官网vue快速上手 vue示例图 请点击下面工程名称,跳转到代码的仓库页面,将工程 下载下来 Demo Code 里有详细的注释 代码:LearnVue...

算法之双指针题型:
双指针例题小总结: 力扣27: 移除元素 力扣题目链接 双指针分为: 快慢双指针:同一个起点,同向出发 相向双指针:从两端出发,方向相反,终会相遇 经典的双指针(快慢双指…...

vue传递给后端时间格式问题
前端处理 首先前端使用moment.js进行处理 data.userEnrolDate moment(data.userEnrolDate).format(YYYY-MM-DD HH:mm:ss);后端处理 JsonFormat(timezone "GMT8", pattern "yyyy-MM-dd HH:mm:ss") DateTimeFormat(pattern "yyyy-MM-dd HH:mm:ss…...

php使用jwt作登录验证
1 在项目根目录下,安装jwt composer require firebase/php-jwt 2 在登录控制器中加入生成token的代码 use Firebase\JWT\JWT; use Firebase\JWT\Key; class Login extends Cross {/*** 显示资源列表** return \think\Response*/public function index(Request $r…...

【zlm】 PTS DTS
在音视频编码和传输中,PTS(Presentation Time Stamp)和DTS(Decoding Time Stamp)是两个关键的时间戳,用于确保音视频帧的顺序和同步。它们在多媒体处理中扮演重要的角色: PTS(Presen…...

【两周学会FPGA】从0到1学习紫光同创FPGA开发|盘古PGL22G开发板学习之DDR3 IP简单读写测试(六)
本原创教程由深圳市小眼睛科技有限公司创作,版权归本公司所有,如需转载,需授权并注明出处 适用于板卡型号: 紫光同创PGL22G开发平台(盘古22K) 一:盘古22K开发板(紫光同创PGL22G开发…...

第6章 内核模块符号导出实验(iTOP-RK3568开发板驱动开发指南 )
在上一小节中,给大家讲解了驱动模块传参实验,使用insmod命令加载驱动时可以进行参数的传递,但是每一个内核模块之间是相互独立的,那模块间的符号传递要怎样进行呢,让我们带着疑问来进行本章节的学习吧! 6.…...