【Spring全家桶系列】Spring中的事务管理(基于注解完成实现)

news2024/11/26 2:53:25

⭐️前面的话⭐️

本文已经收录到《Spring框架全家桶系列》专栏,本文将介绍Spring中的事务管理,事务的概念与作用,以及Spring事务的属性和传播机制。

📒博客主页:未见花闻的博客主页
🎉欢迎关注🔎点赞👍收藏⭐️留言📝
📌本文由未见花闻原创,CSDN首发!
📆首发时间:🌴2023年5月16日🌴
✉️坚持和努力一定能换来诗与远方!
💭推荐书籍:📚《无》
💬参考在线编程网站:🌐牛客网🌐力扣🌐acwing
博主的码云gitee,平常博主写的程序代码都在里面。
博主的github,平常博主写的程序代码都在里面。
🍭作者水平很有限,如果发现错误,一定要及时告知作者哦!感谢感谢!


📌导航小助手📌

    • 1.Spring中事务使用方式
      • 1.1Spring事务简介
      • 1.2Spring事务的使用
        • 1.2.1项目准备
        • 1.2.2Spring事务的使用
    • 2.Spring事务角色
    • 3.Spring事务属性
    • 4.Spring事务的传播机制
      • 4.1Spring事务传播机制
      • 4.2转账日志记录案例实现
    • 5.总结
        • 知识点1:@EnableTransactionManagement
        • 知识点2:@Transactional


封面


1.Spring中事务使用方式

1.1Spring事务简介

前面我们已经介绍了如何在Spring环境中整合mybatis完成数据库的增删查改操作,在正常情况下,操作数据库是没有问题的,但是一个业务需要多次操作数据库,并且需要完成修改,插入,删除操作可能会有问题,如转账,其实是有两个步骤,第一步从A账户扣钱,第二步在B账户中加钱。

如果第一步顺利执行后,在执行完成第二步前,程序发生了异常,那就完了,第二个操作就不能够正常执行了,这导致的结果就是,A的钱少了,B的钱没有增多,这种事情客户是完全不能容忍的,所以我们写的程序也是不能够容忍这样的bug。

为了解决这个问题,Spring引入了事务管理的机制,事务的作用就是:

  • 保证执行一组数据库操作的时候,要么全部失败,要不全部成功,即同成功同失败。

那也就是在程序发生异常的时候,回滚所有已经成功数据库操作,这样就算这一次转账失败了,也不会给客户和商家带来损失。

Spring中事务的作用就是:

  • 保证在数据层或业务层执行的一系列数据库操作同成功同失败。

1.2Spring事务的使用

需求: 实现任意两个账户间转账操作

需求微缩: A账户减钱,B账户加钱

为了实现上述的业务需求,我们可以按照下面步骤来实现下:
①:数据层提供基础操作,指定账户减钱(outMoney),指定账户加钱(inMoney)

②:业务层提供转账操作(transfer),调用减钱与加钱的操作

③:提供2个账号和操作金额执行转账操作

④:基于Spring整合MyBatis环境搭建上述操作

为了使用Spring事务,Spring提供了一个平台事务管理器PlatformTransactionManager,使用该管理器,就能够去使用Spring的事务了。

1
commit是用来提交事务,rollback是用来回滚事务,PlatformTransactionManager只是一个接口,Spring还为其提供了一个具体的实现类DataSourceTransactionManager

下面我们就基于该事务管理器DataSourceTransactionManager来实现Spring中的事务。

1.2.1项目准备

具体的步骤不再多说了,创建Maven项目,导入依赖,配置业务层,数据层,单元测试,整合mybatis。

项目结构如下:

2

项目依赖:

    <!--  spring框架依赖 -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.3.19</version>
    </dependency>
    <!-- druid数据库连接池依赖 -->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.16</version>
    </dependency>
    <!--   mybatis依赖-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.6</version>
    </dependency>
    <!--    spring整合mybatis依赖-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>2.0.7</version>
    </dependency>
    <!--  jdbc依赖  -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.3.15</version>
    </dependency>
    <!--  mysql驱动依赖  -->
    <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.28</version>
    </dependency>
    <!--  lombok依赖 用于快速生成setter getter 和 toString  -->
    <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.26</version>
      <scope>provided</scope>
    </dependency>
    <!-- 整合junit -->
    <!-- https://mvnrepository.com/artifact/junit/junit -->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.13.2</version>
      <scope>test</scope>
    </dependency>
    <!-- spring test环境-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>5.2.10.RELEASE</version>
    </dependency>

数据表:

create database if not exists transfer;
use transfer;
-- 创建账户表
drop table if exists account;
create table account (
    -- 用户编号uid
                         uid int primary key auto_increment,
    -- 姓名
                         username varchar(32),
    -- 余额
                         money double(8, 2) not null default 0
    );

配置类:

Spring配置类:

@Configuration
@PropertySource("classpath:jdbc.properties")
@ComponentScan("com.transfer.demo")
@Import({MybatisConfig.class, JdbcConfig.class})
@EnableTransactionManagement
public class SpringConfig {
}

数据库配置类,包含数据源等信息的配置:

public class JdbcConfig {
    //驱动
    @Value("${jdbc.driver}")
    private String drive;
    //url
    @Value("${jdbc.url}")
    private String url;
    //账户
    @Value("${jdbc.username}")
    private String username;
    //密码
    @Value("${jdbc.password}")
    private String password;

    /**
     * 配置数据源
     */
    @Bean
    public DataSource getDataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl(url);
        dataSource.setDriverClassName(drive);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        //设置数据自动提交
        dataSource.setDefaultAutoCommit(true);
        return dataSource;
    }
    @Bean
    public PlatformTransactionManager getTransactionManger(DataSource dataSource) {
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }
}

mybatis配置类,使用数据源配置SqlSessionFactory对象工厂,配置xml扫描路径,扫描sql相关注解扫描范围:

public class MybatisConfig {
    /**
     * 配置mybatis
     * @param dataSource 数据库的数据源
     * @return SqlSessionFactoryBean
     */
    @Bean
    public SqlSessionFactoryBean getSqlSessionFactoryBean(DataSource dataSource) {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        //配置数据源
        sqlSessionFactoryBean.setDataSource(dataSource);
        //简化
        sqlSessionFactoryBean.setTypeAliasesPackage("com.transfer.demo.mode");
        //配置所需要扫描到的mapper文件
        sqlSessionFactoryBean.setMapperLocations(new ClassPathResource("mapper/TransferMapper.xml"));
        return sqlSessionFactoryBean;
    }

    /**
     * 配置所对应需要扫描的mapper接口 基于注解编写sql需要设置这个 使用xml文件配置编写sql不需要
     * @return  MapperScannerConfigurer
     */
    @Bean
    public MapperScannerConfigurer getMapperScannerConfigurer() {
        MapperScannerConfigurer configurer = new MapperScannerConfigurer();
        configurer.setBasePackage("com.transfer.demo.dao");
        return configurer;
    }
}

properties配置文件:

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/transfer?useSSL=false&serverTimezone=GMT%2B8&characterEncoding=utf-8
jdbc.username=root
jdbc.password=123456

数据层说明,具体代码以及配置文件sql见博主码云仓库,本项目环境也适应了使用注解进行sql语句的编写:
3
业务层说明:

4

项目环境:
Spring版本:5
MySQL版本:8
Java版本:1.8

1.2.2Spring事务的使用

因为mybatis使用的就是jdbc的事务,所以直接配置jdbc的事务即可。

第一步,配置DataSourceTransactionManager类的Bean,前面在数据库配置当中以及配置好数据源了,直接将数据源配进去即可。
5

第二步,在转账功能上加上@Transactional注解,表示将该方法开启Spring事务,建议加在接口上,降低耦合。

6

第三步,在Spring上启动Spring事务,加上@EnableTransactionManagement注解即可。
7
这样就完成spring事务的设计了,下面我们在转账的两个步骤之间加上一个算术异常,来模拟转账的时候出现异常的情况。

8

单元测试:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {SpringConfig.class})
public class TransferTest {
    @Autowired
    private TransferService transferService;
    @Test
    public void SelectAllTest() {
        System.out.println(transferService.selectAll());
    }
    @Test
    public void transferMoneyTest() {
        Account user1 = new Account();
        Account user2 = new Account();
        user1.setUid(1);
        user2.setUid(2);
        System.out.println("转账前:");
        System.out.println(transferService.selectByUid(user1.getUid()));
        System.out.println(transferService.selectByUid(user2.getUid()));
        try{
            transferService.transferMoney(user1, user2, 520);
        } catch (ArithmeticException e) {
            System.out.println("转账异常:");
        }
        System.out.println("转账后:");
        System.out.println(transferService.selectByUid(user1.getUid()));
        System.out.println(transferService.selectByUid(user2.getUid()));
    }
}

当前数据库表信息:
9
测试结果:
10
从结果上来看,通过spring事务,虽然转账的第一次数据库操作成功了,但是因为遇到异常,进行了数据库回滚操作,保证了数据的可靠性。

实际上,并不是所有的异常都可以触发回滚操作,默认情况下,程序发生ERROR或者运行时异常会触发回滚操作,其他异常不会触发,当然这些都是可以通过操作@Transactional注解中的参数来设置的。

2.Spring事务角色

在Spring事务中,有两种身份,一种是事务管理员,另外一种是事务协调员:

  • 事务管理员:Spring事务的发起方,在Spring中通常指的是业务层的开启事务的方法。
  • 事务协调员:Spring事务的参与方,在Spring中通常指的是数据层的方法,也可以是业务层的方法。

Spring事务没有开启的时候,数据层的两个修改操作默认情况下在数据库中分别都会开启一个事务。
11
此时,transferMoney方法没有开启事务,会存在以下的可能:

  • 两个数据库操作都执行正常,顺利完成转账。
  • 执行过程中出现意外,第一个数据库操作成功,第二个数据库操作失败,会导致账户A钱变少,B账户钱没到账的情况,这就会造成数据出错。

Spring事务开启后,transferMoney方法就是事务的发起方,即Spring事务管理员,数据层的两个操作虽然需要开启事务,但Spring会告诉数据层的两个方法,让它们加入到Spring的事务当中来,以便保证数据操作同成功同失败,即原子性,数据层的两个方法就是Spring事务协调员。

12

  • transferMoney上添加了@Transactional注解,在该方法上就会有一个事务T。
  • 数据层的outMoney方法的事务T1加入到transfer的事务T中。
  • 数据层的inMoney方法的事务T2加入到transfer的事务T中。
  • 这样就保证他们在同一个事务中,当业务层中出现异常,整个事务就会回滚,保证数据的准确性。

需要保证事务管理是基于DataSourceTransactionManagerSqlSessionFactoryBean使用的是同一个数据源。

3.Spring事务属性

前面我们提到了,并不是所有的异常都会引发回滚操作,如默认情况下遇到IOException事务就不会回滚,如果需要指定其他异常引发回滚或不回滚是可以通过配置@Transactional注解的rollbackFor或者noRollbackFor属性进行配置。

@Transactional注解相关属性如下表:

属性作用示例
readOnly设置是否为只读事务readOnly=true只读事务
timeout设置事务超时时间timeout =-1(永不超时,默认)
rollbackFor设置事务回滚异常(class)rollbackFor = NullPointException. class
rollbackForClassName设置事务回滚异常(String)同上格式为字符串
noRollbackFor设置事务不回滚异常(class)noRollbackFor = NullPointException. class
noRollbackForClassName设置事务不回滚异常(String)同上格式为字符串
isolation设置事务隔离级别isolation = Isolation. DEFAULT
propagation设置事务传播行为-

说明:

  • readOnly:true只读事务,false读写事务,增删改要设为false,查询设为true。
  • timeout:设置超时时间单位秒,在多长时间之内事务没有提交成功就自动回滚,-1表示不设置超时时间。
  • rollbackFor:当出现指定异常进行事务回滚,不影响错误和运行时异常进行事务回滚。
  • noRollbackFor:当出现指定异常不进行事务回滚,可以将某种会引发回滚的异常设置为不回滚。
  • rollbackForClassName等同于rollbackFor,只不过属性为异常的类全名字符串。
  • noRollbackForClassName等同于noRollbackFor,只不过属性为异常的类全名字符串。
  • isolation设置事务的隔离级别:
    • DEFAULT :默认隔离级别, 会采用数据库的隔离级别
    • READ_UNCOMMITTED : 读未提交
    • READ_COMMITTED : 读已提交
    • REPEATABLE_READ : 重复读取
    • SERIALIZABLE: 串行化

重点属性就是rollbackFor属性,设置遇到哪一些异常就会回滚事务,默认情况是遇到Error异常RuntimeException异常及其子类就会自动回滚。

示例1,将运行时异常设置为不回滚。

@Transactional(noRollbackFor = {ArithmeticException.class})

示例2,将非运行时异常和非错误的异常设置为引发回滚。

@Transactional(rollbackFor = {IOException.class})

4.Spring事务的传播机制

4.1Spring事务传播机制

前面我们说过Spring事务中,开启事务的时候,事务管理员所在方法中所有的其他事务默认情况下都会加入该事务,成为事务协调员,但是有时候并不需要将里面所有的事务都加入到事务管理员所开启的事务当中,为了解决这种情况,我们可以调整Spring中事务的传播机制,Spring中传播机制的种类有如下几种:

13

REQUIRED: 需要事务,外部存在事务融入当前事务,外部没有事务,开启新的事务。
SUPPORTS: 支持事务,外部存在事务融入当前事务,外部没有事务,不开启新的事务。
REQUIRES_NEW: 每次开启新的事务,如果外部存在事务外部事务挂起,开启新的事务运行,运行结束后回到外部事务。
NOT_SUPPORTED: 不支持事务,如果外部存在事务外部事务挂起,以非事务方式运行。
NEVER: 不支持事务,存在事务报错。
MANDATORY: 强制事务,没有事务报错。
NESTED: 嵌套事务,数据库不支持。

通过设置@Transactional注解中的propagation属性就可以完成Spring事务传播机制的修改。

前面实现了一个转账的案例,在实际的转账的过程中都会存在转账记录,对于转账记录,无论转账是否成功都需要记录,如果使用默认的事务传播机制,一旦转账失败,转账记录就无法存入到数据库当中。所以对于日志记录的事务需要新建一个事务单独运行,需要采用REQUIRES_NEW的事务传播机制。

4.2转账日志记录案例实现

下面我们在转账的基础上加上这样一个记录日志的要求,并实现。

需求:记录转账过程中的转账记录,不论成功还是失败都需要记录,不影响转账操作的原子性。
解决方案:调整日志记录的事务传播机制,当转账事务开启的时候,不加入该事务。

第一步,准备一张表,用来记录转账日志信息,设计如下:
14

lid表示日志的编号,content表示转账细节信息,state表示转账是成功还是失败。

建表语句如下:

use transfer;
-- 创建日志表
drop table if exists account_log;
create table account_log (
    -- 日志id
    lid int primary key auto_increment,
    -- 日志内容
    content varchar(128),
    -- 状态
    state varchar(16)
);

insert into account_log values(null, '测试', '成功');

第二步,编写数据层接口,至少有日志的插入和查询方法,基于注解就直接在数据层写sql即可,配置文件实现,需要另外再写一个配置文件用来写sql,不论哪种都要保证mybatis都能够扫描到。
15
对应业务层实现:

16

第三步,编写日志插入和查询操作的业务层方法,在转账操作中加入转账日志记录功能,因为不论转账失败还是成功都需要记录,所以日志插入可以写在finally中。

为了方便判断转账操作是否成功,我们将转账的两部数据操作放入到同一个try中,然后使用一个变量flag来表示转账操作是否成功,如果执行完第二步转账操作没有出现异常,就将flag置为true

17

这样还不够,在存转账日志的时候,因为不论转账成功还是失败都需要存入转账记录,那么日志插入操作的事务必须单独开启,需要在插入日志的service方法设置事务传播属性为REQUIRES_NEW
18

第四步,编写单元测试代码并验证。

单元测试代码如下,就不解释了,按照自己的思路写个验证程序即可。

19
测试结果:

20

5.总结

知识点1:@EnableTransactionManagement

名称@EnableTransactionManagement
类型配置类注解
位置配置类定义上方
作用设置当前Spring环境中开启注解式事务支持

知识点2:@Transactional

名称@Transactional
类型接口注解 类注解 方法注解
位置业务层接口上方 业务层实现类上方 业务方法上方
作用为当前业务层方法添加事务(如果设置在类或接口上方则类或接口中所有方法均添加事务)
  1. @Transactional 注解只能用在public 方法上,如果用在protected或者private的方法上,不会报错,但是该注解不会生效。
  2. @Transactional注解只能回滚非检查型异常,具体为RuntimeException及其子类和Error子类,可以从Spring源码的DefaultTransactionAttribute类里找到判断方法rollbackOn。
  3. 使用rollbackFor 属性来定义回滚的异常类型,使用 propagation 属性定义事务的传播行为。如: 回滚Exception类的异常,事务的传播行为支持当前事务,当前如果没有事务,那么会创建一个事务。
  4. @Transactional注解不能回滚被try{}catch() 捕获的异常。
  5. @Transactional注解只能对在被Spring 容器扫描到的类下的方法生效。

其实Spring事务的创建也是有一定的规则,对于一个方法里已经存在的事务,Spring 也提供了解决方案去进一步处理存在事务,通过设置@Tranasctional的propagation 属性定义Spring 事务的传播规则。


觉得文章写得不错的老铁们,点赞评论关注走一波!谢谢啦!

1-99

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

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

相关文章

怀念浩哥(左耳朵耗子),一个纯粹的技术人

上周末听闻浩哥的事了&#xff0c;期初还不信。在网上搜索消息&#xff0c;看来是真的。他才四十多岁&#xff0c;觉得非常可惜。很早就关注过浩哥&#xff0c;他是一位正直纯粹和爱分享的技术大牛。无论是技术分享还是人生感悟&#xff0c;或者是成长相关&#xff0c;都让我学…

Portainer: 带你领略强大且易用的容器管理平台

什么是Portainer? Portainer是一个强大的容器管理平台 Portainer是一款轻量级的应用,它提供了图形化界面,用于方便地管理Docker环境,包括单机环境和集群环境。Portainer全球最受欢迎的容器管理平台,拥有超过100万用户和24,600颗GitHub星Portainer的定位及与周边生态的交互…

【Redis】聊一下持久化机制-AOF

前言 持久化其实在任何存储系统中&#xff0c;都是避不开的话题&#xff0c;比如数据库系统就有ACID进行数据、日志的持久化。将文件写入到内存、缓存、磁盘中。在比如消息队列Kafka也有消息的持久化机制&#xff0c;为防止数据的丢失也需要将数据持久化存储。目的其实就是为了…

javaweb系列- JavaScript事件

1.6 JavaScript事件 1.6.1 事件介绍 如下图所示的百度注册页面&#xff0c;当我们用户输入完内容&#xff0c;百度可以自动的提示我们用户名已经存在还是可以使用。那么百度是怎么知道我们用户名输入完了呢&#xff1f;这就需要用到JavaScript中的事件了。 什么是事件呢&…

【SpringBoot】整合第三方技术Junit. MybatisPlus druid

【SpringBoot】整合第三方技术 整合junit整合MyBatis整合Mybatis-plus使用阿里云创建工程 SpringBoot整合druid 整合junit 自己定义一个功能&#xff0c;测试功能接口 测试步骤注入你要测试的对象 提前声明为bean资源执行你要测试的方法 package com.ustc.sp7;import com.us…

MySQL—MVCC

文章目录 数据库并发的场景有三种MVCC概念读-写3个记录隐藏列字段undo log模拟MVCC Read ViewRC与RR的本质区别RCRR 数据库并发的场景有三种 读-读: 不存在任何问题&#xff0c;也不需要并发控制 读-写∶有线程安全问题&#xff0c;可能会造成事务隔离性问题&#xff0c;可能遇…

阿里云GPU服务器租用费用包年包月、一个小时和学生价格

阿里云GPU服务器租用价格表包括包年包月、一个小时收费以及学生GPU服务器租用费用&#xff0c;阿里云GPU计算卡包括NVIDIA V100计算卡、T4计算卡、A10计算卡和A100计算卡&#xff0c;GPU云服务器gn6i可享受3折&#xff0c;阿里云百科分享阿里云GPU服务器租用价格表、GPU一个小时…

chatgpt赋能Python-aidlearning安装python

安装Python的必要性和意义 Python是一种非常流行的编程语言&#xff0c;它的易学易用&#xff0c;高效实用&#xff0c;使得很多人选择使用Python进行编程。对于AI学习&#xff0c;Python更是至关重要&#xff0c;因为很多AI框架和算法都需要使用Python进行编写和实现。因此&a…

Java设计模式 14-访问者模式

访问者模式 这个模式用的很少&#xff0c;《设计模式》的作者评价为&#xff1a; 大多情况下&#xff0c;你不需要使用访问者模式&#xff0c;但是一旦需要使用它时&#xff0c;那就真的需要使用了 一、测评系统的需求 1)将观众分为男人和女人&#xff0c;对歌手进行测评&…

逻辑回归优化技巧总结(全)

本文从实际应用出发&#xff0c;以数据特征、优化算法、模型优化等方面&#xff0c;全面地归纳了逻辑回归&#xff08;LR&#xff09;优化技巧。 一、LR的特征生成 逻辑回归是简单的广义线性模型&#xff0c;模型的拟合能力很有限&#xff0c;无法学习到特征间交互的非线性信息…

详解c++---继承

目录标题 为什么会有继承继承的概念继承的定义基类和派生类对象赋值转换继承中的作用域派生类的默认成员函数继承和友元继承与静态成员什么是菱形继承如何解决菱形继承解决的底层原理继承和组合 为什么会有继承 在平时的使用过程中通常会出现一部分数据会在很多其他地方被使用…

微信商城小程序怎么开通?

随着移动互联网的快速发展&#xff0c;微信商城小程序成为了越来越多企业开启电商新模式的首选之一。微信商城小程序不仅在运营方式上具有很大优势&#xff0c;还能够提供更加便利和舒适的购物体验。那么微信商城小程序怎么开通呢&#xff1f;下面&#xff0c;我们将简单介绍微…

ARM嵌入式编译之循环操作(LOOP)优化

取决于循环的迭代次数&#xff0c;完成循环可能需要花费大量时间&#xff0c;此外&#xff0c;每次迭代时&#xff0c;需要判断循环条件是否成立&#xff0c;这一操作也会降低循环的性能。 1 循环展开-Loop unrolling 为了减少每次循环都需要判断迭代条件带来的性能影响&…

chatgpt赋能Python-20210712用python提取年月日

使用Python实现年月日的提取 在进行数据分析或开发中&#xff0c;提取年月日信息是非常重要的一项任务。而Python作为一门优秀的编程语言&#xff0c;其内置的多种库和函数可以帮助我们快速且高效地实现这一任务。本篇文章将会介绍使用Python提取年月日的方法以及注意事项。 …

串口与4g模块

1.认识4G模块 1、接线与插卡&#xff1a;EC03-DNC4G通信模块&#xff0c;生产的公司是EBYTE&#xff08;亿佰特&#xff09;。官网上有软件和用户手册下载地址。 保证插卡不插错&#xff0c;一是使用SIM卡卡套&#xff0c;二是方向保证正确。如图位置是SIM卡状态灯&#xff0…

Trie树

目录 一、Tire树的概念二、Trie树的应用Trie字符串统计最大异或对&#xff08;难点&#xff09;暴力遍历法Trie树优化法 一、Tire树的概念 Trie树又称字典树、单词查找树。是一种能够高效存储和查找字符串集合的数据结构。 示例&#xff1a; 利用Tire树的数据结构储存字符串 储…

跟庄买股票得新技巧(第三弹)集合竞价战法

尾盘抢筹&#xff08;参考昨天&#xff09; 57分 12.35 收盘价 12.42 股价明显上涨&#xff08;越大越好&#xff09;全天阳线&#xff0c;否则突然变高就有作线的嫌疑12.35到12.42&#xff0c;滞留大量为成交单&#xff08;买一到买十存在大量买单&#xff0c;否则有做线嫌疑…

Spring-boot 结合Thymeleaf--拦截器--文件上传

目录 Spring-boot 结合Thymeleaf 官方文档 基本介绍 ● Thymeleaf 是什么 ● Thymeleaf 的优点 ● Thymeleaf 的缺点 ● Thymeleaf 机制说明 Thymeleaf 语法 表达式 1. 表达式一览 2.字面量 3. 文本操作 4.运算符 3. 比较运算 4. 条件运算 th 属性 迭代 条件运…

4年经验面试要15K,一问自动化却以为我在刁难他?

我这次是公司招聘的面试官之一&#xff0c;主要负责一些技术上的考核&#xff0c;这段时间还真让我碰到了不少奇葩求职者 昨天公司的HR小席刚跟我吐槽&#xff1a;这个星期没有哪天不加班的&#xff01;各种招聘网站上的消息源源不断&#xff0c;连吃饭都要回消息…… 看来最…

【Java面试】JVM垃圾回收相关知识点

文章目录 1. Java中对象有哪些引用类型&#xff1f;2. 有哪些基本的垃圾回收算法&#xff1f;3. 什么是分区收集算法&#xff1f;和分代收集什么区别&#xff1f;4. 什么是Minor GC、Major GC、Full GC?5. 什么情况下会触发Full GC&#xff1f; 1. Java中对象有哪些引用类型&a…