【JavaEE进阶】——Spring事务和事务传播机制

news2024/9/21 15:36:22

目录

🚩事务

🎈为什么需要事务?

🎈事务的操作

🚩Spring 中事务的实现

🎈数据准备

🎈Spring 编程式事务(了解)

🎈Spring 声明式事务 @Transactional

🍭@Transactional 详解

📝rollbackFor

❗结论(异常捕获问题)

📝spring事务隔离级别

📝Spring 事务传播机制

✅Propagation.REQUIRED(事务之间互相影响)

✅Propagation.REQUIRED_NEW(新建事务,互不影响)

✅Propagation.NEVER (不⽀持当前事务, 抛异常)

✅Propagation.NESTED(嵌套事务)

👩🏻‍💻NESTED和REQUIRED有什么区别?

🚩总结


🚩事务

事务是一组操作的集合,它是一个不可分割的工作单位,这些操作要么同时成功,要么同时失败。

🎈为什么需要事务?

我们在进行程序的开发的时候,也会有事务的需求。

比如转账的操作:

第⼀步:A 账⼾ -100 元.
第⼆步:B 账⼾ +100 元.
如果没有事务,第⼀步执⾏成功了, 第⼆步执⾏失败了, 那么A 账⼾的100 元就平⽩⽆故消失了. 如果使⽤事务就可以解决这个问题, 让这⼀组操作要么⼀起成功, 要么⼀起失败.

🎈事务的操作

事务的操作主要有三步:
1. 开启事务: start transaction/ begin (⼀组操作前开启事务)
2. 提交事务: commit (这组操作全部成功, 提交事务)
3. 回滚事务: rollback (这组操作中间任何⼀个操作出现异常, 回滚事务)
-- 开启事务
start transaction;
-- 提交事务
commit ;
-- 回滚事务
rollback ;

🚩Spring 中事务的实现

前⾯课程我们讲了MySQL的事务操作, Spring对事务也进⾏了实现
Spring 中的 事务操作 分为两类:
1. 编程式事务(⼿动写代码操作事务).
2. 声明式事务(利⽤注解⾃动开启和提交事务)
在学习事务之前,我们先准备数据和数据的访问代码。

🎈数据准备

0>需求: ⽤⼾注册, 注册时在⽇志表中插⼊⼀条操作记录:
1>数据准备:
-- 数据库
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';
2>代码准备:
1. 创建项⽬ spring-trans, 引⼊Spring Web, Mybatis, mysql等依赖
2. 配置⽂件
spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/trans_test?characterEncoding=utf8&useSSL=false
    username: 用户名
    password: 密码
    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 #日志配置
server:
  port: 9090
3. 实体类
@Data
public class LogInfo {
    private Integer id;
    private String userName;
    private String op;
    private Data createTime;
    private Data updateTime;
}
@Data
public class UserInfo {
    private Integer id;
    private String userName;
    private String password;
    private Data createTime;
    private Data updateTime;
}
4. Mapper
@Mapper
public interface UserInfoMapper {
    @Insert("insert into user_info (user_name,password)values (#{userName},#{password})")
    Integer insert(String userName,String password);
}
@Mapper
public interface LogInfoMapper {
    @Insert("insert into log_info (user_name,op)values (#{userName},#{op})")
    Integer insertLog(String userName,String op);
}
5. Service
@Service
public class LogService {
    @Autowired
    private LogInfoMapper logInfoMapper;

    public Integer insert(String userName, String op) {
        Integer result= logInfoMapper.insertLog(userName,op);
        return result;
    }
}
@Service
public class UserService {
    @Autowired
    private UserInfoMapper userInfoMapper;
    public Integer insert(String userName, String password) {
        return userInfoMapper.insert(userName,password);
    }
}

🎈Spring 编程式事务(了解)

Spring ⼿动操作事务和上⾯ MySQL 操作事务类似, 有 3 个重要操作步骤:
  • 开启事务(获取事务)
  • 提交事务
  • 回滚事务

SpringBoot 内置了两个对象:
  • 1. DataSourceTransactionManager 事务管理器. ⽤来获取事务(开启事务), 提交或回滚事务
  • 2. TransactionDefinition 是事务的属性, 在获取事务的时候需要将 TransactionDefinition 传递进去从⽽获得⼀个事务 TransactionStatus
@RestController
@Slf4j
@RequestMapping("/user")
public class UserController {
    @Autowired
    private DataSourceTransactionManager dataSourceTransactionManager;//事务管理器
    @Autowired
    private TransactionDefinition transactionDefinition;//定义事务属性
    @Autowired
    private UserService userService;
    @RequestMapping("/register")
    public Boolean register(String userName, String password){
        //开启事务
        TransactionStatus transaction=dataSourceTransactionManager.getTransaction(transactionDefinition);
        //用户注册
        Integer result=userService.insert(userName,password);
        System.out.println("插入用户表,result: "+result);
        //提交事务
        dataSourceTransactionManager.commit(transaction);
        return true;
    }
}

提交事务:

事务回滚:数据库信息未更新

@RestController
@Slf4j
@RequestMapping("/user")
public class UserController {
    @Autowired
    private DataSourceTransactionManager dataSourceTransactionManager;//事务管理器
    @Autowired
    private TransactionDefinition transactionDefinition;//定义事务属性
    @Autowired
    private UserService userService;
    @RequestMapping("/register")
    public Boolean register(String userName, String password){
        //开启事务
        TransactionStatus transaction=dataSourceTransactionManager.getTransaction(transactionDefinition);
        //用户注册
        Integer result=userService.insert(userName,password);
        System.out.println("插入用户表,result: "+result);
        //回滚事务
        dataSourceTransactionManager.rollback(transaction);
        //提交事务
        dataSourceTransactionManager.commit(transaction);
        return true;
    }
}


🎈Spring 声明式事务 @Transactional

声明式事务的实现很简单, 只需要在需要事务的⽅法上 添加 @Transactional 注解 就可以实现了.
⽆需⼿动开启事务和提交事务, 进⼊⽅法时⾃动开启事务, ⽅法执⾏完成会⾃动提交事务, 如果中途发⽣了没有处理的异常会⾃动回滚事务.
程序运行成功,事务正常提交
@RestController
@Slf4j
@RequestMapping("/user3")
public class UserController3 {
    @Autowired
    private UserService userService;
    @Transactional
    @RequestMapping("/register")
    public Boolean register(String userName, String password){
        userService.insert(userName,password);
        return true;
    }
}

再注册方法上加上@Transactional 注解即可。

程序运行错误,事务自动回滚:
@RestController
@Slf4j
@RequestMapping("/user3")
public class UserController3 {
    @Autowired
    private UserService userService;
    @Transactional
    @RequestMapping("/register")
    public Boolean register(String userName, String password){
        userService.insert(userName,password);
        //强制程序抛出异常
        int a = 10/0;
        return true;
    }
}

数据库内容未更新。


@Transactional 作⽤
@Transactional 可以⽤来修饰⽅法或类:
  • 修饰⽅法时: 只有修饰public ⽅法时才⽣效(修饰其他⽅法时不会报错, 也不⽣效)[推荐]
  • 修饰类时: 对 @Transactional 修饰的类中所有的 public ⽅法都⽣效.

⽅法/类被 @Transactional 注解修饰时, 在⽬标⽅法执⾏开始之前, 会⾃动开启事务, ⽅法执⾏结束之后, ⾃动提交事务.
  • 如果在⽅法执⾏过程中, 出现异常, 且异常未被捕获, 就进⾏事务回滚操作.

——如上面直接强制程序抛出异常(也就是异常未被捕获,那么事务回滚)

  • 如果异常被程序捕获, ⽅法就被认为是成功执⾏, 依然会提交事务.
@RestController
@Slf4j
@RequestMapping("/user3")
public class UserController3 {
    @Autowired
    private UserService userService;
    @Transactional
    @RequestMapping("/register")
    public Boolean register(String userName, String password){
        userService.insert(userName,password);
        log.info("用户注册成功");
        //对异常进⾏捕获
        try {
            //强制程序抛出异常
            int a = 10/0;
        }catch (Exception e){
            e.printStackTrace();
        }
        return true;
    }
}

事务未回滚,依旧提交事务。


如果程序中,出现错误,我们依旧需要事务进行回滚,不提交事务,该如何处理呢?

如果需要事务进⾏回滚, 有以下两种⽅式:
1. 重新抛出异常
@RestController
@Slf4j
@RequestMapping("/user3")
public class UserController3 {
    @Autowired
    private UserService userService;
    @Transactional
    @RequestMapping("/register")
    public Boolean register(String userName, String password){
        userService.insert(userName,password);
        log.info("用户注册成功");
        //对异常进⾏捕获
        try {
            //强制程序抛出异常
            int a = 10/0;
        }catch (Exception e){
            throw e;
        }
        return true;
    }
}
2. ⼿动回滚事务
使⽤ TransactionAspectSupport.currentTransactionStatus() 得到当前的事务, 并
使⽤ setRollbackOnly 设置 setRollbackOnly
@RestController
@Slf4j
@RequestMapping("/user3")
public class UserController3 {
    @Autowired
    private UserService userService;
    @Transactional
    @RequestMapping("/register")
    public Boolean register(String userName, String password){
        userService.insert(userName,password);
        log.info("用户注册成功");
        //对异常进⾏捕获
        try {
            //强制程序抛出异常
            int a = 10/0;
        }catch (Exception e){
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        return true;
    }
}

🍭@Transactional 详解

通过上⾯的代码, 我们学习了 @Transactional 的基本使⽤. 接下来我们学习 @Transactional
注解的使⽤细节.
我们主要学习 @Transactional 注解当中的三个常⻅属性:
  • 1. rollbackFor: 异常回滚属性. 指定能够触发事务回滚的异常类型. 可以指定多个异常类型
  • 2. Isolation: 事务的隔离级别. 默认值为 Isolation.DEFAULT
  • 3. propagation: 事务的传播机制. 默认值为 Propagation.REQUIRED

📝rollbackFor
@Transactional 默认只在遇到运⾏时异常和Error时才会回滚, ⾮运⾏时异常不回滚. 即
Exception的⼦类中, 除了RuntimeException及其⼦类.
🔴我们上面说,如果程序有异常,我们需要给回滚,那么就有两种方式进行回滚,第一就是手动回滚,第二就是重新抛出异常。
🔴但是再第二种方法中, @Transactional默认只在遇到运行时异常和Error时才会回滚,上面我们举例的是int a=1/0这是算术异常属于运行时异常中的一类,但是如果我们 抛出IOException异常也就是非运行时异常时,事务就直接提交,不回滚。接下来我们由代码显示。
@RestController
@Slf4j
@RequestMapping("/user3")
public class UserController3 {
    @Autowired
    private UserService userService;
    @Transactional
    @RequestMapping("/register")
    public Boolean register(String userName, String password) throws IOException {
        userService.insert(userName,password);
        log.info("用户注册成功");
        //对异常进⾏捕获
        try {
            //强制程序抛出异常
            int a = 10/0;
        }catch (Exception e){
            throw new IOException();
        }
        return true;
    }
}

发现虽然程序抛出了异常, 但是事务依然进⾏了提交。


解决: 如果我们需要所有异常都回滚, 需要来配置 @Transactional 注解当中的 rollbackFor 属性, 通 过 rollbackFor 这个属性指定出现何种异常类型时事务进⾏回滚.
  @Transactional(rollbackFor = Exception.class)
@RestController
@Slf4j
@RequestMapping("/user3")
public class UserController3 {
    @Autowired
    private UserService userService;
    @Transactional(rollbackFor = Exception.class)
    @RequestMapping("/register")
    public Boolean register(String userName, String password) throws IOException {
        userService.insert(userName,password);
        log.info("用户注册成功");
        //对异常进⾏捕获
        try {
            //强制程序抛出异常
            int a = 10/0;
        }catch (Exception e){
            throw new IOException();
        }
        return true;
    }
}


❗结论(异常捕获问题)
  • 在Spring的事务管理中,默认只在遇到运⾏时异常RuntimeException和Error时才会回滚.
  • 如果需要回滚指定类型的异常, 可以通过rollbackFor属性来指定.   @Transactional(rollbackFor = Exception.class)


📝spring事务隔离级别
MySQL 事务隔离级别(回顾)
SQL 标准定义了四种隔离级别, MySQL 全都⽀持. 这四种隔离级别分别是:
  • 1. 读未提交(READ UNCOMMITTED): 读未提交, 也叫未提交读. 该隔离级别的事务可以看到其他事务中未提交的数据. 因为其他事务未提交的数据可能会发⽣回滚, 但是该隔离级别却可以读到, 我们把该级别读到的数据称之为脏数据, 这个问题称之为脏读.
  • 2. 读提交(READ COMMITTED): 读已提交, 也叫提交读. 该隔离级别的事务能读取到已经提交事务的数据, 该隔离级别不会有脏读的问题.但由于在事务的执⾏中可以读取到其他事务提交的结果, 所以在不同时间的相同 SQL 查询可能会得到不同的结果, 这种现象叫做不可重复读
  • 3. 可重复读(REPEATABLE READ): 事务不会读到其他事务对已有数据的修改, 即使其他事务已提交. 也就可以确保同⼀事务多次查询的结果⼀致, 但是其他事务新插⼊的数据, 是可以感知到的. 这也就引发了幻读问题. 可重复读, 是 MySQL 的默认事务隔离级别. ⽐如此级别的事务正在执⾏时, 另⼀个事务成功的插⼊了某条数据, 但因为它每次查询的结果都是⼀样的, 所以会导致查询不到这条数据, ⾃⼰重复插⼊时⼜失败(因为唯⼀约束的原因). 明明在事务中查询不到这条信息,但⾃⼰就是插⼊不进去, 这个现象叫幻读.
  • 4. 串⾏化(SERIALIZABLE): 序列化, 事务最⾼隔离级别. 它会强制事务排序, 使之不会发⽣冲突, 从⽽解决了脏读, 不可重复读和幻读问题, 但因为执⾏效率低, 所以真正使⽤的场景并不多.
Spring 事务隔离级别
Spring 中事务隔离级别有5 种:
  • 1. Isolation.DEFAULT : 以连接的数据库的事务隔离级别为主.
  • 2. Isolation.READ_UNCOMMITTED : 读未提交, 对应SQL标准中 READ UNCOMMITTED
  • 3. Isolation.READ_COMMITTED : 读已提交,对应SQL标准中 READ COMMITTED
  • 4. Isolation.REPEATABLE_READ : 可重复读, 对应SQL标准中 REPEATABLE READ
  • 5. Isolation.SERIALIZABLE : 串⾏化, 对应SQL标准中 SERIALIZABLE


📝Spring 事务传播机制
事务传播机制就是: 多个事务⽅法存在调⽤关系时, 事务是如何在这些⽅法间进⾏传播的
⽐如有两个⽅法A, B都被 @Transactional 修饰, A⽅法调⽤B⽅法
A⽅法运⾏时, 会开启⼀个事务. 当A调⽤B时, B⽅法本⾝也有事务, 此时B⽅法运⾏时, 是加⼊A的事务, 还是创建⼀个新的事务呢?
这个就涉及到了事务的传播机制
⽐如公司流程管理
执⾏任务之前, 需要先写执⾏⽂档, 任务执⾏结束, 再写总结汇报
此时A部⻔有⼀项⼯作, 需要B部⻔的⽀援, 此时B部⻔是直接使⽤A部⻔的⽂档, 还是新建⼀个⽂档呢?
事务隔离级别解决的是多个事务同时调⽤⼀个数据库的问题
⽽事务传播机制解决的是⼀个事务在多个节点(⽅法)中传递的问题

事务的传播机制分类:
@Transactional 注解⽀持事务传播机制的设置, 通过 propagation 属性来指定传播⾏为.
Spring 事务传播机制有以下 7 种: 方法A调用方法B   A->B
  • 1. Propagation.REQUIRED : 默认的事务传播级别. 如果当前存在事务, 则加⼊该事务. 如果当前没有事务, 则创建⼀个新的事务.

  • 2. Propagation.SUPPORTS : 如果当前存在事务, 则加⼊该事务. 如果当前没有事务, 则以⾮事务的⽅式继续运⾏.

  • 3. Propagation.MANDATORY :强制性. 如果当前存在事务, 则加⼊该事务. 如果当前没有事务, 则抛出异常.

  • 4. Propagation.REQUIRES_NEW : 创建⼀个新的事务. 如果当前存在事务, 则把当前事务挂起. 也就是说不管外部⽅法是否开启事务, Propagation.REQUIRES_NEW 修饰的内部⽅法都会新开启⾃⼰的事务, 且开启的事务相互独⽴, 互不⼲扰.

  • 5. Propagation.NOT_SUPPORTED : 以⾮事务⽅式运⾏, 如果当前存在事务, 则把当前事务挂起(不⽤).

  • 6. Propagation.NEVER : 以⾮事务⽅式运⾏, 如果当前存在事务, 则抛出异常.

  • 7. Propagation.NESTED : 如果当前存在事务, 则创建⼀个事务作为当前事务的嵌套事务来运⾏.如果当前没有事务, 则该取值等价于 PROPAGATION_REQUIRED .

1,4,7不存在事务创建事务

2,5 不存在事务以非事务运行

3,6 存在事务就抛出异常


✅Propagation.REQUIRED(事务之间互相影响)
默认的事务传播级别. 如果当前存在事务, 则加⼊该事务. 如果当前没有事务, 则创建⼀个新的事务.
对应的UserService和LogService都添加上
@Transactional(propagation = Propagation.REQUIRED )
@Service
public class UserService {
    @Autowired
    private UserInfoMapper userInfoMapper;
   @Transactional(propagation = Propagation.REQUIRED)
    public Integer insert(String userName, String password) {
        return userInfoMapper.insert(userName,password);
    }
}
@Service
public class LogService {
    @Autowired
    private LogInfoMapper logInfoMapper;
    @Transactional(propagation = Propagation.REQUIRED)
    public Integer insert(String userName, String op) {
        Integer result= logInfoMapper.insertLog(userName,op);
        return result;
    }
}
@RequestMapping("/user2")
@RestController
@Slf4j
public class UserController2 {
    @Autowired
    private UserService userService;
    @Autowired
    private LogService logService;
    @Transactional
    @RequestMapping("/register")
    public Boolean register(String userName, String password){
        /**
         * 用户表和日志表的插入,理应在service中完成
         */
        Integer result=userService.insert(userName,password);
        System.out.println("插入用户表,result: "+result);
        //插入日志表
        Integer logResult=logService.insert(userName,"用户注册");
        System.out.println("插入日志表,result:"+logResult);
        return true;
    }
}

从上面看程序成功,事务提交,用户表和日志表都更新。

在LogService中加入异常,查看运行结果

我们观察运行结果事务回滚,数据库中两个表都没有更新,因为它们是处于同一个事务中。

运⾏程序, 发现数据库没有插⼊任何数据.
流程描述:
  • 1. register⽅法开始事务
  • 2. ⽤⼾注册, 插⼊⼀条数据 (执⾏成功) (和register使⽤同⼀个事务)
  • 3. 记录操作⽇志, 插⼊⼀条数据(出现异常, 执⾏失败) (和register使⽤同⼀个事务)
  • 4. 因为步骤3出现异常, 事务回滚. 步骤2和3使⽤同⼀个事务, 所以步骤2的数据也回滚了.
所以看出required事务传播级别,不管是多少个方法,都共存在一个事务中只要存在事务,没有事务就创建事务。如果其中一个出现异常,那么都回滚,只有都执行成功,那么事务提交。

✅Propagation.REQUIRED_NEW(新建事务,互不影响)
创建⼀个新的事务. 如果当前存在事务, 则把当前事务挂起. 也就是说不管外部⽅法是否开启事务, Propagation.REQUIRES_NEW 修饰的 内部⽅法都会新开启⾃⼰的事务 , 且开启的事务 相互独⽴, 互不⼲扰..
所以,userservice中的方法就会创建一个事务,logservice中的方法也会创建一个事务,那么就相当于有三个事务。三个事务相互独立,互不干扰。
UserService方法中的事务程序执行正常
LogService方法中的事务程序执行异常
我们看看是否是相互独立提交和回滚的。
如果LogSerice失败,UserService成功,两个方法是独立的,互不影响。用户表更新,日志表更新失败。
当我们不希望事务之间相互影响时, 可以使⽤该传播⾏为

✅NEVER (不⽀持当前事务, 抛异常)
修改UserService 中对应⽅法的事务传播机制为 Propagation. NEVER

程序执⾏报错, 没有数据插⼊。在以后应用中,如果不希望该事务加入,那么就用never。

✅NESTED(嵌套事务)
将上述UserService 和LogService 中相关⽅法事务传播机制改为 Propagation. NESTED
1.三个事务全部成功
三个事务全部成功,那么结果就和Required一样,用户表和日志表数据库都更新成功。

2.三个事务中部分失败

LogService中事务程序会出现异常,UserService事务程序没有异常。
三个事务都回滚,结果也是和Required一样,用户表和日志表更新失败。
register事务可以认为是⽗事务, 嵌套事务是⼦事务. ⽗事务出现异常, ⼦事务也会回滚, ⼦事务出现异常, 如果不进⾏处理, 也会导致⽗事务回滚。

👩🏻‍💻NESTED和REQUIRED有什么区别?
和REQUIRED区别就是,NESTED可以实现部分回滚。我们在上面LogService方法出现错误,如果没有进行捕获事务会和REQUIRED一样进行回滚,我们上面也说明了异常捕获问题,如果有异常捕获了那么就正常提交,但是我们希望异常捕获后回滚,那么就需要我们手动回滚异常或者抛出异常。这种做法会和REQUIRED一样嘛?
首先看 NESTED事务传播机制
用户表更新,日志表未更新。实现了部分回滚。

对比REQUIRED传播事务机制

重新运⾏程序, 发现⽤⼾表和⽇志表的数据添加都失败了.
REQUIRED 如果回滚就是回滚所有事务, 不能实现部分事务的回滚. (因为属于同⼀个事务)

  • 整个事务如果全部执⾏成功, ⼆者的结果是⼀样的.
  • 如果事务⼀部分执⾏成功, REQUIRED加⼊事务会导致整个事务全部回滚. NESTED嵌套事务可以实现局部回滚, 不会影响上⼀个⽅法中执⾏的结果.
嵌套事务之所以能够实现部分事务的回滚, 是因为事务中有⼀个保存点(savepoint)的概念, 嵌套事务进⼊之后相当于新建了⼀个保存点, ⽽滚回时只回滚到当前保存点
REQUIRED 是加⼊到当前事务中, 并没有创建事务的保存点, 因此出现了回滚就是整个事务回滚, 这就是嵌套事务和加⼊事务的区别.

🚩总结

  • 1. Spring中使⽤事务, 有两种⽅式: 编程式事务(⼿动操作)和声明式事务. 其中声明式事务使⽤较多,在⽅法上添加 @Transactional 就可以实现了
  • 2. 通过 @Transactional(isolation = Isolation.SERIALIZABLE) 设置事务的隔离级别. Spring 中的事务隔离级别有 5 种
  • 3. 通过 @Transactional(propagation = Propagation.REQUIRED) 设置事务的传播机制, Spring 中的 事务传播级别有 7 种, 重点关注 REQUIRED (默认值,公用一个事务,事务之间相互影响) 和 REQUIRES_NEW(几个方法就是几个事务,事务之间互不影响,相互独立)

事情一步一步的来。

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

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

相关文章

2013年全国大学生数学建模竞赛B题碎纸片复原(含word论文和源代码资源)

文章目录 一、部分题目二、部分论文三、部分源代码四、完整word版论文和源代码(两种获取方式) 一、部分题目 2013高教社杯全国大学生数学建模竞赛题目 B题 碎纸片的拼接复原 破碎文件的拼接在司法物证复原、历史文献修复以及军事情报获取等领域都有着重…

基于术语词典干预的机器翻译挑战赛笔记Task2 #Datawhale AI 夏令营

上回: 基于术语词典干预的机器翻译挑战赛笔记Task1 跑通baseline Datawhale AI 夏令营-CSDN博客文章浏览阅读718次,点赞11次,收藏8次。基于术语词典干预的机器翻译挑战赛笔记Task1 跑通baselinehttps://blog.csdn.net/qq_23311271/article/d…

统计一个页面用到的html,css,js

<!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>统计html</title><style>* {margin: …

【人工智能】AI音乐创作兴起与AI伦理的新视角

文章目录 &#x1f34a;AI音乐创作&#xff1a;一键生成&#xff0c;打造你的专属乐章&#x1f34a;1 市面上的AI音乐应用1.1 Suno AI1.2 网易天音 &#x1f34a;2 AI音乐创作的流程原理(直接制作可跳到第3点)2.1 AI音乐流派2.2 AI音乐风格2.3 AI音乐的结构顺序2.5 选择AI音乐乐…

基于PSO算法优化PID参数的一些问题

目录 前言 Q1&#xff1a;惯性权重ω如何设置比较好&#xff1f;学习因子C1和C2如何设置&#xff1f; Q2&#xff1a;迭代速度边界设定一定能够遍历&#xff08;/覆盖&#xff09;整个PID参数二维空间范围吗&#xff1f;还是说需要与迭代次数相关&#xff1f;迭代次数越高&a…

阵列信号处理学习笔记(一)--阵列信号处理定义

阵列信号 阵列信号处理学习笔记&#xff08;一&#xff09;–阵列信号处理定义 阵列信号处理学习笔记&#xff08;二&#xff09;–空域滤波基本原理 文章目录 阵列信号前言一、阵列信号处理定义1.1 信号1.2 阵列 二、雷达数据中哪些属于空间采样总结 前言 MOOC 阵列信号处理…

Bubbliiiing 的 Retinaface rknn python推理分析

Bubbliiiing 的 Retinaface rknn python推理分析 项目说明 使用的是Bubbliiiing的深度学习教程-Pytorch 搭建自己的Retinaface人脸检测平台的模型&#xff0c;下面是项目的Bubbliiiing视频讲解地址以及源码地址和博客地址&#xff1b; 作者的项目讲解视频&#xff1a;https:…

数据结构排序合集(笔记)

目录 一.插入排序与希尔排序 二.选择排序与堆排序 三.冒泡排序和快速排序 四.归并排序 五.计数排序 一.插入排序与希尔排序 时间复杂度空间复杂度稳定性插入排序O&#xff08;N^2&#xff09;O(1)稳定希尔排序O(N^1.3)O(1)不稳定 插入排序&#xff1a; 希尔排序&#xff…

css-01-如何实现“文本过长(文本在一行排),超出部分被省略号代替”

目录 需求代码代码解释 需求 最近写一个项目&#xff0c;遇到了一个问题&#xff0c;就是希望实现下面这种文字效果&#xff1a;文字在一行排&#xff0c;超出的部分用省略号代替 代码 <!DOCTYPE html> <html lang"en"><head><meta charset…

OCC 创建点线面体

目录 一、利用封装已有算法实现 1、盒子建模算法封装 2、可视化 二、利用OCC 点线面实现 1、实现过程 2、实现一个面 3、拉伸面生成体 4、旋转面生成体 三、总结 一、利用封装已有算法实现 1、盒子建模算法封装 BRepPrimAPI_MakeBox box(2, 2, 2); 2、可视化 void VTK…

Gateway源码分析:路由Route、断言Predicate、Filter

文章目录 源码总流程图说明GateWayAutoConfigurationDispatcherHandlergetHandler()handleRequestWith()RouteToRequestUrlFilterReactiveLoadBalancerClientFilterNettyRoutingFilter 补充知识适配器模式 详细流程图 源码总流程图 在线总流程图 说明 Gateway的版本使用的是…

在线 PDF 制作者泄露用户上传的文档

两家在线 PDF 制作者泄露了数万份用户文档&#xff0c;包括护照、驾驶执照、证书以及用户上传的其他个人信息。 我们都经历过这样的情况&#xff1a;非常匆忙&#xff0c;努力快速制作 PDF 并提交表单。许多人向在线 PDF 制作者寻求帮助&#xff0c;许多人的祈祷得到了回应。 …

Python学习笔记—100页Opencv详细讲解教程

目录 1 创建和显示窗口... - 4 - 2 加载显示图片... - 6 - 3 保存图片... - 7 - 4 视频采集... - 8 - 5视频录制... - 11 - 6 控制鼠标... - 12 - 7 TrackBar 控件... - 14 - 8.RGB和BGR颜色空间... - 16 - 9.HSV和HSL和YUV.. - 17 - 10 颜色空间的转化... - 18 - …

分页查询与分页条件查询

--------------- 无PageHelper插件分页查询 1.创建PageBean实体类 Data NoArgsConstructor AllArgsConstructor public class PageBean<T> {private Long total;//总条数private List<T> items;//当前页数据集合 }类型安全性 泛型&#xff1a;提供了编译时的类型…

【学长工具库】1.如何快速部署开源框架 | 若依框架保姆级搭建教程

今天学长带来了一款十分适合自学的开源框架-若依框架&#xff0c; 本文会详细的教大家怎么部署这个系统。 文末有所有资料获取方式~ 框架技术栈 前端采用 Vue、Element UI。后端采用 Spring Boot、Spring Security、Redis & Jwt。权限认证使用 Jwt&#xff0c;支持多终端…

【IEEE出版,会议历史良好、论文录用检索快】第四届计算机科学与区块链国际学术会议 (CCSB 2024,9月6-8)

CCSB 2024会议由深圳大学主办&#xff0c;旨在探讨计算机科学的最新发展如何与区块链技术相结合&#xff0c;以及这一结合如何推动金融、供应链管理、数据安全和其他多个行业的革新&#xff0c; 本次会议将提供一个多学科交流的平台&#xff0c;汇集来自相关领域学者的研究和思…

vxe-弹窗初始化激活选中Vxe-Table表格中第一行input输入框

1.实现效果 2.Modal弹窗的渲染过程 一、Vue组件的生命周期 Vue组件从创建到销毁会经历一系列的生命周期钩子&#xff0c;这些钩子为开发者提供了在不同阶段插入自定义逻辑的机会。在Modal弹窗的上下文中&#xff0c;这些生命周期钩子同样适用。 beforeCreate&#xff1a;组件…

解决 Ubuntu 用户登录后的 shell 和功能问题

在使用 Ubuntu 系统管理用户时&#xff0c;可能会遇到一些常见的问题&#xff0c;比如新创建的用户无法使用常见命令&#xff08;如 ll&#xff09;以及输出信息没有颜色。这些问题通常与用户的默认 shell 有关。本文将总结如何解决这些问题&#xff0c;并确保新用户能够正常使…

【linux深入剖析】命名管道 | 匿名管道与命名管道的区别 | system V共享内存

&#x1f341;你好&#xff0c;我是 RO-BERRY &#x1f4d7; 致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f384;感谢你的陪伴与支持 &#xff0c;故事既有了开头&#xff0c;就要画上一个完美的句号&#xff0c;让我们一起加油 目录 1. 命名管道2. 创建命名管…

STM32之九:ADC模数转换器

目录 1. 简介 2. ADC 2.1 逐次逼近型寄存器SAR 2.2 ADC转换时间 3 ADC框图 3.1 8 bit ADC0809芯片内部框图 3.2 ADC框图 3.2.1 注入通道和规则通道 3.2.2 单次/连续转换模式 3.2.3 扫描模式 3.2.4 外部触发转换 3.2.5 数据对齐 3.2.6 模拟看门狗 4. 总结和ADC驱…