Spring 事务和事务传播机制

✏️作者:银河罐头
📋系列专栏:JavaEE

🌲“种一棵树最好的时间是十年前,其次是现在”

Spring 中事务的实现

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

  1. 编程式事务(⼿动写代码操作事务)。
  2. 声明式事务(利⽤注解⾃动开启和提交事务)。

事务在 MySQL 有 3 个重要的操作:开启事务、提交事务、回滚事务,它们对应的操作命令如下:

-- 开启事务
start transaction;
-- 业务执⾏-- 提交事务
commit;-- 回滚事务
rollback;

Spring 编程式事务

Spring ⼿动操作事务和上⾯ MySQL 操作事务类似,它也是有 3 个重要操作步骤:

开启事务(获取事务)。

提交事务。

回滚事务。

image-20230606203219409

application.properties:

spring.datasource.url= jdbc:mysql://localhost:3306/mycnblog?characterEncoding=utf8&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#设置 MyBatis
mybatis.mapper-locations=classpath:/mybatis/*Mapper.xml
#打印 MyBatis 执行的 sql
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#因为打印 MyBatis 执行的 sql 日志级别是 debug,而默认级别是 info,所以要修改日志的默认级别为 debug
logging.level.com.example.demo=debug
package com.example.demo.controller;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;//编程式事务@Autowiredprivate DataSourceTransactionManager transactionManager;@Autowiredprivate TransactionDefinition transactionDefinition;@RequestMapping("/del")public int del(Integer id){if(id != null && id > 0){// 开启事务TransactionStatus transactionStatus =transactionManager.getTransaction(transactionDefinition);// 业务操作: 删除用户int result = userService.del(id);System.out.println(result);// 提交事务
//            transactionManager.commit(transactionStatus);// 回滚事务transactionManager.rollback(transactionStatus);return result;}return 0;}
}
mysql> select * from userinfo;
+----+----------+----------+-------+---------------------+---------------------+-------+
| id | username | password | photo | createtime          | updatetime          | state |
+----+----------+----------+-------+---------------------+---------------------+-------+
|  1 | admin    | 123456   |       | 2021-12-06 17:10:48 | 2021-12-06 17:10:48 |     1 |
|  2 | zhangsan | 123456   |       | 2023-05-29 20:10:14 | 2023-05-29 20:10:14 |     1 |
|  3 | lisi     | 123456   |       | 2023-05-29 20:27:53 | 2023-05-29 20:27:53 |     1 |
|  4 | wangwu   | 123456   |       | 2023-05-30 14:43:23 | 2023-05-30 14:43:23 |  NULL |
|  5 | wangwu2  | 123456   |       | 2023-05-30 14:44:30 | 2023-05-30 14:44:30 |     1 |
| 13 | liliu    | 123456   |       | 2023-05-30 15:57:37 | 2023-05-30 15:57:37 |     1 |
+----+----------+----------+-------+---------------------+---------------------+-------+
6 rows in set (0.00 sec)

image-20230607090646410

再次查询数据库:

mysql> select * from userinfo;
+----+----------+----------+-------+---------------------+---------------------+-------+
| id | username | password | photo | createtime          | updatetime          | state |
+----+----------+----------+-------+---------------------+---------------------+-------+
|  1 | admin    | 123456   |       | 2021-12-06 17:10:48 | 2021-12-06 17:10:48 |     1 |
|  2 | zhangsan | 123456   |       | 2023-05-29 20:10:14 | 2023-05-29 20:10:14 |     1 |
|  3 | lisi     | 123456   |       | 2023-05-29 20:27:53 | 2023-05-29 20:27:53 |     1 |
|  4 | wangwu   | 123456   |       | 2023-05-30 14:43:23 | 2023-05-30 14:43:23 |  NULL |
|  5 | wangwu2  | 123456   |       | 2023-05-30 14:44:30 | 2023-05-30 14:44:30 |     1 |
| 13 | liliu    | 123456   |       | 2023-05-30 15:57:37 | 2023-05-30 15:57:37 |     1 |
+----+----------+----------+-------+---------------------+---------------------+-------+
6 rows in set (0.00 sec)

发现 id = 13 这条数据还在,就是因为回滚操作。

@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;//编程式事务@Autowiredprivate DataSourceTransactionManager transactionManager;@Autowiredprivate TransactionDefinition transactionDefinition;@RequestMapping("/del")public int del(Integer id){if(id == null || id <= 0){return 0;}// 开启事务TransactionStatus transactionStatus = null;// 业务操作: 删除用户int result = 0;try{transactionStatus =transactionManager.getTransaction(transactionDefinition);result = userService.del(id);System.out.println("删除: " + result);// 提交事务/回滚事务transactionManager.commit(transactionStatus); // 提交事务}catch (Exception e){if(transactionStatus != null) {transactionManager.rollback(transactionStatus); // 回滚事务}}return result;}
}

Spring 声明式事务

声明式事务的实现很简单,只需要在需要的⽅法上添加 @Transactional 注解就可以实现了,⽆需⼿动 开启事务和提交事务,进⼊⽅法时⾃动开启事务,⽅法执⾏完会⾃动提交事务,如果中途发⽣了没有处理的异常会⾃动回滚事务。

@Transactional 在单元测试中使用,无论结果如何都会 rollback;

@Transactional 在 普通方法中使用,没有出现异常就会提交事务,如果出现异常才会 rollback.

@RestController
@RequestMapping("/user2")
public class UserController2 {@Autowiredprivate UserService userService;@RequestMapping("/del")@Transactionalpublic int del(Integer id){if(id == null || id <= 0){return 0;}return userService.del(id);}
}

image-20230607093736244

image-20230607093751056

image-20230607093810036

id = 5 的这条数据 成功删除。

  • 下面来验证 "回滚"效果:
@RestController
@RequestMapping("/user2")
public class UserController2 {@Autowiredprivate UserService userService;@RequestMapping("/del")@Transactionalpublic int del(Integer id){if(id == null || id <= 0){return 0;}int result = userService.del(id);System.out.println("删除: " + result);int num = 10/0;return result;}
}
mysql>  select * from userinfo;
+----+----------+----------+-------+---------------------+---------------------+-------+
| id | username | password | photo | createtime          | updatetime          | state |
+----+----------+----------+-------+---------------------+---------------------+-------+
|  1 | admin    | 123456   |       | 2021-12-06 17:10:48 | 2021-12-06 17:10:48 |     1 |
|  2 | zhangsan | 123456   |       | 2023-05-29 20:10:14 | 2023-05-29 20:10:14 |     1 |
|  3 | lisi     | 123456   |       | 2023-05-29 20:27:53 | 2023-05-29 20:27:53 |     1 |
|  4 | wangwu   | 123456   |       | 2023-05-30 14:43:23 | 2023-05-30 14:43:23 |  NULL |
+----+----------+----------+-------+---------------------+---------------------+-------+
4 rows in set (0.00 sec)

image-20230607094625351

@Transactional 作⽤范围

@Transactional 可以⽤来修饰⽅法或类:

修饰⽅法时:需要注意只能应⽤到 public ⽅法上,否则不⽣效。

修饰类时:表明该注解对该类中所有的 public ⽅法都⽣效。

@Transactional 参数说明

image-20230607100905576

Spring 事务隔离级别

Spring 中事务隔离级别包含以下 5 种:

  1. Isolation.DEFAULT:以连接的数据库的事务隔离级别为主。
  2. Isolation.READ_UNCOMMITTED:读未提交,可以读取到未提交的事务,存在脏读。
  3. Isolation.READ_COMMITTED:读已提交,只能读取到已经提交的事务,解决了脏读,存在不可重 复读。
  4. Isolation.REPEATABLE_READ:可重复读,解决了不可重复读,但存在幻读(MySQL默认级 别)。
  5. Isolation.SERIALIZABLE:串⾏化,可以解决所有并发问题,但性能太低。

可以看出,相比于 MySQL 的事务隔离级别,Spring 的事务隔离级别只是多了⼀个 Isolation.DEFAULT(以数据库的全局事务隔离级别为主)。

image-20230607163631990

事务类型:

1.普通事务

2.只读事务,没设置事务隔离级别的情况下(可重复读) => 可以设置隔离级别

3.无事务(默认的隔离级别可重复读)

@Transactional(readOnly = true, isolation = Isolation.SERIALIZABLE)
  • 再来看一个例子:
@RestController
@RequestMapping("/user2")
public class UserController2 {@Autowiredprivate UserService userService;@RequestMapping("/del")@Transactional(readOnly = true, isolation = Isolation.SERIALIZABLE)public int del(Integer id){if(id == null || id <= 0){return 0;}int result = userService.del(id);System.out.println("删除: " + result);try {int num = 10/0;} catch (Exception e) {e.printStackTrace();}return result;}
}

在 int num = 10/0; 语句外面加 try - catch ,事务还会 rollback 吗?

image-20230607185941608

mysql> select * from userinfo;
+----+----------+----------+-------+---------------------+---------------------+-------+
| id | username | password | photo | createtime          | updatetime          | state |
+----+----------+----------+-------+---------------------+---------------------+-------+
|  1 | admin    | 123456   |       | 2021-12-06 17:10:48 | 2021-12-06 17:10:48 |     1 |
|  2 | zhangsan | 123456   |       | 2023-05-29 20:10:14 | 2023-05-29 20:10:14 |     1 |
|  3 | lisi     | 123456   |       | 2023-05-29 20:27:53 | 2023-05-29 20:27:53 |     1 |
+----+----------+----------+-------+---------------------+---------------------+-------+
3 rows in set (0.00 sec)

image-20230607190038288

事务直接提交了,没有 rollback。删除了一条数据。

声明式事务发生异常,并添加 try-catch 有可能出现异常,事务不会自动回滚,那么就会导致业务出错。

解决方案有 2 种:

1.将异常抛出去,让框架感知到异常,框架感知到异常之后会自动回滚事务。

@RestController
@RequestMapping("/user2")
public class UserController2 {@Autowiredprivate UserService userService;@RequestMapping("/del")@Transactionalpublic int del(Integer id){if(id == null || id <= 0){return 0;}int result = userService.del(id);System.out.println("删除: " + result);try {int num = 10/0;} catch (Exception e) {throw e;}return result;}
}

image-20230607191050448

image-20230607191123723

image-20230607191147004

回滚了。

2.通过代码的方式手动回滚事务。

@RestController
@RequestMapping("/user2")
public class UserController2 {@Autowiredprivate UserService userService;@RequestMapping("/del")@Transactionalpublic int del(Integer id){if(id == null || id <= 0){return 0;}int result = userService.del(id);System.out.println("删除: " + result);try {int num = 10/0;} catch (Exception e) {// 手动回滚事务TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}return result;}
}

image-20230607191606380

image-20230607191706919

image-20230607191641489

手动回滚成功。

面试题:Spring 事务失效的场景有哪些?

类没有修饰符,默认是 default,

接口没有修饰符,默认是 public.

Spring 事务传播机制

Spring 事务传播机制定义了多个包含了事务的⽅法,相互调⽤时,事务是如何在这些⽅法间进⾏传递 的。

事务隔离级别是保证多个并发事务执⾏的可控性(稳定性的),⽽事务传播机制是保证⼀个事务在多个调⽤⽅法间传递的可控性。

事务隔离级别解决的是多个事务同时调⽤⼀个数据库的问题。

image-20230607194330428

⽽事务传播机制解决的是⼀个事务在多个节点(⽅法)中传递的问题。

image-20230607194438392

事务传播机制

Spring 事务传播机制包含以下 7 种:

  1. Propagation.REQUIRED:默认的事务传播级别,它表示如果当前存在事务,则加入该事务;如果当前没有事务,则创建⼀个新的事务。
  2. Propagation.SUPPORTS:如果当前存在事务,则加⼊该事务;如果当前没有事务,则以⾮事务的 ⽅式继续运⾏。
  3. Propagation.MANDATORY:(mandatory:强制性)如果当前存在事务,则加⼊该事务;如果当 前没有事务,则抛出异常。
  4. Propagation.REQUIRES_NEW:表示创建⼀个新的事务,如果当前存在事务,则把当前事务挂 起。也就是说不管外部⽅法是否开启事务,Propagation.REQUIRES_NEW 修饰的内部⽅法会新开 启⾃⼰的事务,且开启的事务相互独⽴,互不⼲扰。
  5. Propagation.NOT_SUPPORTED:以⾮事务⽅式运⾏,如果当前存在事务,则把当前事务挂起。
  6. Propagation.NEVER:以⾮事务⽅式运⾏,如果当前存在事务,则抛出异常。
  7. Propagation.NESTED:如果当前存在事务,则创建⼀个事务作为当前事务的嵌套事务来运⾏;如 果当前没有事务,则该取值等价于 PROPAGATION_REQUIRED。

以上7种传播机制,可根据“是否支持当前事务”的维度分为一下3类:

img

接下来我们用一个例子,来说明这3类事务传播机制的区别:

以情侣之间是否买房为例,我们将以上3类事务传播机制看作是恋爱中的3类女生类型:

  • 普通型
  • 强势型
  • 懂事型

这三类女生如下图:

img

演示事务传播机制

1.支持当前事务 Propagation.REQUIRED.

“一荣俱荣一损俱损”.

package com.example.demo.controller;
@RestController
@RequestMapping("/user3")
public class UserController3 {@Autowiredprivate UserService userService;@RequestMapping("/add")@Transactional(propagation = Propagation.REQUIRED)public int add(String username, String password){if(null == username || null == password || username.equals(" ") || password.equals(" ")){return 0;}UserInfo userInfo = new UserInfo();userInfo.setUsername(username);userInfo.setPassword(password);int result = userService.add(userInfo);return result;}
}
package com.example.demo.service;@Service
public class UserService {@Autowiredprivate UserMapper userMapper;@Autowiredprivate LogService logService;public int del(Integer id){return userMapper.del(id);}@Transactional(propagation = Propagation.REQUIRED)public int add(UserInfo userInfo){//给用户表添加用户信息int addUserResult = userMapper.add(userInfo);System.out.println("添加用户结果: " + addUserResult);//添加日志信息Log log = new Log();log.setMessage("添加用户信息");logService.add(log);return addUserResult;}
}
package com.example.demo.service;@Service
public class LogService {@Autowiredprivate LogMapper logMapper;@Transactional(propagation = Propagation.REQUIRED)public int add(Log log){int result = logMapper.add(log);System.out.println("添加日志结果: " + result);int num = 10/0;return result;}
}
mysql> select * from userinfo;
+----+----------+----------+-------+---------------------+---------------------+-------+
| id | username | password | photo | createtime          | updatetime          | state |
+----+----------+----------+-------+---------------------+---------------------+-------+
|  1 | admin    | 123456   |       | 2021-12-06 17:10:48 | 2021-12-06 17:10:48 |     1 |
|  2 | zhangsan | 123456   |       | 2023-05-29 20:10:14 | 2023-05-29 20:10:14 |     1 |
|  3 | lisi     | 123456   |       | 2023-05-29 20:27:53 | 2023-05-29 20:27:53 |     1 |
+----+----------+----------+-------+---------------------+---------------------+-------+
3 rows in set (0.01 sec)mysql>  select * from log;
Empty set (0.00 sec)

image-20230617214541900

image-20230617214646066

image-20230617214754389

算数异常,log 和 userinfo 都回滚了。

Propagation.REQUIRED:默认的事务传播级别,它表示如果当前存在事务,则加入该事务;如果当前没有事务,则创建⼀个新的事务。

2.不支持当前事务 Propagation.REQUIRES_NEW.

把上面例子调用链中所有的 Propagation.REQUIRED 都改成 Propagation.REQUIRES_NEW.

预期结果是 添加日志失败,添加用户成功。

image-20230618102531362

image-20230618102821127

发现 添加用户操作也回滚了?!和预期不符。因为UserController 感知到了异常,整个调用链都回滚了。

  • 为了演示 添加日志失败,添加用户成功 这种效果。把代码稍微改动。
@RestController
@RequestMapping("/user3")
public class UserController3 {@Autowiredprivate UserService userService;@RequestMapping("/add")@Transactional(propagation = Propagation.REQUIRES_NEW)public int add(String username, String password){if(null == username || null == password || username.equals(" ") || password.equals(" ")){return 0;}UserInfo userInfo = new UserInfo();userInfo.setUsername(username);userInfo.setPassword(password);int result = userService.add(userInfo);return result;}
}
@Service
public class UserService {@Autowiredprivate UserMapper userMapper;@Autowiredprivate LogService logService;public int del(Integer id){return userMapper.del(id);}@Transactional(propagation = Propagation.REQUIRES_NEW)public int add(UserInfo userInfo){//给用户表添加用户信息int addUserResult = userMapper.add(userInfo);System.out.println("添加用户结果: " + addUserResult);//添加日志信息Log log = new Log();log.setMessage("添加用户信息");logService.add(log);return addUserResult;}
}
@Service
public class LogService {@Autowiredprivate LogMapper logMapper;@Transactional(propagation = Propagation.REQUIRES_NEW)public int add(Log log){int result = logMapper.add(log);System.out.println("添加日志结果: " + result);//回滚操作TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();return result;}
}

image-20230618103224050

image-20230618103242655

image-20230618103305198

日志回滚添加失败,用户没有回滚添加成功。

mysql> delete from userinfo where id = 5;
Query OK, 1 row affected (0.00 sec)mysql> select * from userinfo;
+----+----------+----------+-------+---------------------+---------------------+-------+
| id | username | password | photo | createtime          | updatetime          | state |
+----+----------+----------+-------+---------------------+---------------------+-------+
|  1 | admin    | 123456   |       | 2021-12-06 17:10:48 | 2021-12-06 17:10:48 |     1 |
|  2 | zhangsan | 123456   |       | 2023-05-29 20:10:14 | 2023-05-29 20:10:14 |     1 |
|  3 | lisi     | 123456   |       | 2023-05-29 20:27:53 | 2023-05-29 20:27:53 |     1 |
+----+----------+----------+-------+---------------------+---------------------+-------+
3 rows in set (0.00 sec)
  • 把调用链中所有的 Propagation.REQUIRES_NEW 都改成 Propagation.REQUIRED.

image-20230618103948758

  • 为什么这里会报错?

日志(内层事务)要求回滚,用户(外层事务)没有感知到异常要提交事务,二者矛盾。

image-20230618104005764

image-20230618104043098

用户和日志都回滚了。

  • 对于 Propagation.REQUIRED,如果外部事物回滚,那么内部事务也会回滚。但是不会报错。
@RestController
@RequestMapping("/user3")
public class UserController3 {@Autowiredprivate UserService userService;@RequestMapping("/add")@Transactional(propagation = Propagation.REQUIRED)public int add(String username, String password){if(null == username || null == password || username.equals(" ") || password.equals(" ")){return 0;}UserInfo userInfo = new UserInfo();userInfo.setUsername(username);userInfo.setPassword(password);int result = userService.add(userInfo);//回滚操作TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();return result;}
}
@Service
public class UserService {@Autowiredprivate UserMapper userMapper;@Autowiredprivate LogService logService;public int del(Integer id){return userMapper.del(id);}@Transactional(propagation = Propagation.REQUIRED)public int add(UserInfo userInfo){//给用户表添加用户信息int addUserResult = userMapper.add(userInfo);System.out.println("添加用户结果: " + addUserResult);//添加日志信息Log log = new Log();log.setMessage("添加用户信息");logService.add(log);return addUserResult;}
}
@Service
public class LogService {@Autowiredprivate LogMapper logMapper;@Transactional(propagation = Propagation.REQUIRED)public int add(Log log){int result = logMapper.add(log);System.out.println("添加日志结果: " + result);return result;}
}

image-20230618105300497

没有报错。

mysql> select * from log;
Empty set (0.00 sec)mysql>  select * from userinfo;
+----+----------+----------+-------+---------------------+---------------------+-------+
| id | username | password | photo | createtime          | updatetime          | state |
+----+----------+----------+-------+---------------------+---------------------+-------+
|  1 | admin    | 123456   |       | 2021-12-06 17:10:48 | 2021-12-06 17:10:48 |     1 |
|  2 | zhangsan | 123456   |       | 2023-05-29 20:10:14 | 2023-05-29 20:10:14 |     1 |
|  3 | lisi     | 123456   |       | 2023-05-29 20:27:53 | 2023-05-29 20:27:53 |     1 |
+----+----------+----------+-------+---------------------+---------------------+-------+
3 rows in set (0.00 sec)

而且 用户和日志都回滚。

3.嵌套事务 Propagation.NESTED.

@RestController
@RequestMapping("/user3")
public class UserController3 {@Autowiredprivate UserService userService;@RequestMapping("/add")@Transactional(propagation = Propagation.NESTED)public int add(String username, String password){if(null == username || null == password || username.equals(" ") || password.equals(" ")){return 0;}UserInfo userInfo = new UserInfo();userInfo.setUsername(username);userInfo.setPassword(password);int result = userService.add(userInfo);return result;}
}
@Service
public class UserService {@Autowiredprivate UserMapper userMapper;@Autowiredprivate LogService logService;public int del(Integer id){return userMapper.del(id);}@Transactional(propagation = Propagation.NESTED)public int add(UserInfo userInfo){//给用户表添加用户信息int addUserResult = userMapper.add(userInfo);System.out.println("添加用户结果: " + addUserResult);//添加日志信息Log log = new Log();log.setMessage("添加用户信息");logService.add(log);return addUserResult;}
}
@Service
public class LogService {@Autowiredprivate LogMapper logMapper;@Transactional(propagation = Propagation.NESTED)public int add(Log log){int result = logMapper.add(log);System.out.println("添加日志结果: " + result);//回滚操作TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();return result;}
}

预期结果:日志回滚,用户不回滚。添加日志失败,添加用户成功。

image-20230618105927945

image-20230618110039895

image-20230618110003172

总结:

1.REQUIRED

image-20230618111743988

是一个整体。如果外部事物回滚,那么内部事务也会回滚,不报错;如果内部事务回滚,那么外部事务也会回滚,报错。

2.REQUIRES_NEW

无论如何都会新建一个事务。

image-20230618112108089

外部事务和内部事务相互独立,互不影响。

3.NESTED

image-20230618112210950

虽然是嵌套关系,

但是外部事务和内部事务相互独立,互不影响。

嵌套事务 NESTED 原理

嵌套事务只所以能够实现部分事务的回滚,是因为事务中有⼀个保存点(savepoint)的概念,嵌套事务 进⼊之后相当于新建了⼀个保存点,⽽滚回时只回滚到当前保存点,因此之前的事务是不受影响的。

⽽ REQUIRED 是加⼊到当前事务中,并没有创建事务的保存点,因此出现了回滚就是整个事务回滚, 这就是嵌套事务和加⼊事务的区别。

MySQL :: MySQL 5.7 Reference Manual :: 13.3.4 SAVEPOINT, ROLLBACK TO SAVEPOINT, and RELEASE SAVEPOINT Statements

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

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

相关文章

x264 deblock filter 代码解读

在x264源码里&#xff0c;void x264_frame_deblock_row( x264_t *h, int mb_y )函数中定义了如下的宏片段&#xff0c;这段代码旨在完成对MB的deblocking 操作&#xff0c;其中针对edge 取不同的值的时候&#xff0c;有的做deblocking&#xff0c; 有的不做&#xff0c;看这部分…

Web概述

1.1 程序开发架构 1.1.1C/S体系结构介绍 C/S是Client/Server的缩写&#xff0c;即客户端&#xff0f;服务器结构。在这种结构中&#xff0c;服务器 通常采用高性能的机或工作站&#xff0c;并采用大型数据库系统&#xff08;如Oracle或SQLServer)客户端 则需要安装专用的客户…

【SQL应知应会】分析函数的点点滴滴(一)

欢迎来到爱书不爱输的程序猿的博客, 本博客致力于知识分享&#xff0c;与更多的人进行学习交流 本文收录于SQL应知应会专栏,本专栏主要用于记录对于数据库的一些学习&#xff0c;有基础也有进阶&#xff0c;有MySQL也有Oracle 分析函数的点点滴滴 1.什么是分析函数&#xff1a;…

【系统开发】尚硅谷 - 谷粒商城项目笔记(七):消息队列

文章目录 消息队列概述两大种类RabbitMQ安装及基操Docker中安装添加用户创建Virtual Hosts设置权限添加交换机创建队列交换机绑定队列 五种消息模型SpringBoot整合MQ引入依赖properties配置开启RabbitMQ API使用创建交换机创建队列交换机绑定队列发送消息接收消息 消息确认机制…

【Spring Cloud Alibaba Seata 处理分布式事务】——每天一点小知识

&#x1f4a7; S p r i n g C l o u d A l i b a b a S e a t a 处理分布式事务 \color{#FF1493}{Spring Cloud Alibaba Seata 处理分布式事务} SpringCloudAlibabaSeata处理分布式事务&#x1f4a7; &#x1f337; 仰望天空&#xff0c;妳我亦是行人.✨ &#x1f98…

基于Elasticsearch + Fluentd + Kibana(EFK)搭建日志收集管理系统

目录 1、EFK简介 2、EFK框架 2.1、Fluentd系统架构 2.2、Elasticsearch系统架构 2.3、Kibana系统架构 3、Elasticsearch接口 4、EFK在虚拟机中安装步骤 4.1、安装elasticsearch 4.2、安装kibana 4.3、安装fluentd 4.4、进入kibana创建索引 5、Fluentd配置介绍 Elas…

WWDC 23 之后的 SwiftUI 有哪些新功能

文章目录 前言数据流动画ScrollView搜索新手势新增的小功能总结 前言 WWDC 23 已经到来&#xff0c;SwiftUI 框架中有很多改变和新增的功能。在本文中将主要介绍 SwiftUI 中数据流、动画、ScrollView、搜索、新手势等功能的新变化。 数据流 Swift 5.9 引入了宏功能&#xff…

【FPGA】Verilog:时序电路 | 触发器电路 | 上升沿触发 | 同步置位 | 异步置位

前言&#xff1a;本章内容主要是演示Vivado下利用Verilog语言进行电路设计、仿真、综合和下载 示例&#xff1a;触发器电路 ​ 功能特性&#xff1a; 采用 Xilinx Artix-7 XC7A35T芯片 配置方式&#xff1a;USB-JTAG/SPI Flash 高达100MHz 的内部时钟速度 存储器&#xff1a;…

VS2022 And QtCreator10 调试 Qt 源码教程

文章目录 背景IDE 调试 Qt 源码Visual Studio 2022Qt Creator 10.0.1 排查思路姊妹篇系列 简 述&#xff1a; 记录使用 Visual Studo 2022 和 QtCreator10 调试 Qt 5.15 源码和 加载 .pdb 的方法。 本文初发于 “偕臧的小站”&#xff0c;同步转载于此。 背景 源码&#xff1a;…

软考A计划-系统集成项目管理工程师-项目范围管理(一)

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff…

C# Excel 表列序号

171 Excel 表列序号 给你一个字符串 columnTitle &#xff0c;表示 Excel 表格中的列名称。返回 该列名称对应的列序号 。 例如&#xff1a; A -> 1 B -> 2 C -> 3 … Z -> 26 AA -> 27 AB -> 28 … 示例 1: 输入: columnTitle “A” 输出: 1 示例 2: …

数据库原理之数据库事物

文章目录 一、事物介绍1.1 事物的目的是保证数据的一致性1.2 事物的ACID A、I、D是为了实现 C1.3 什么是本地事物(Local Transactions) 二、数据库系统如何实现ACID2.1 影响深远的ARIES理论2.2 本地事物如何实现原子性和持久性 A、D2.2.1 实现原子性和持久性的Commit Logging方…