SSM框架学习-Spring事务管理入门

news2024/12/25 9:32:44

文章目录

  • 前言
  • 六、Spring事务
    • 1.Spring事务简介
    • 2.入门案例--模拟银行间转账业务
    • 3.开启Spring事务的一般步骤
    • 4.Spring事务角色
    • 5.spring事务属性--rollbackfor
    • 6.入门案例进阶--转账业务追加日志
    • 7. Spring事务属性--事务传播行为
  • 总结


前言

为了巩固所学的知识,作者尝试着开始发布一些学习笔记类的博客,方便日后回顾。当然,如果能帮到一些萌新进行新技术的学习那也是极好的。作者菜菜一枚,文章中如果有记录错误,欢迎读者朋友们批评指正。
(博客的参考源码可以在我主页的资源里找到,如果在学习的过程中有什么疑问欢迎大家在评论区向我提出)

六、Spring事务

1.Spring事务简介

一个数据库事务是一个被视为单一的工作单元的操作序列。这些操作应该要么完整地执行,要么完全不执行。事务管理是一个重要组成部分,RDBMS 面向企业应用程序,以确保数据完整性和一致性。事务的概念可以描述为具有以下四个关键属性说成是 ACID:

原子性:事务应该当作一个单独单元的操作,这意味着整个序列操作要么是成功,要么是失败的。

一致性:这表示数据库的引用完整性的一致性,表中唯一的主键等。

隔离性:可能同时处理很多有相同的数据集的事务,每个事务应该与其他事务隔离,以防止数据损坏。

持久性:一个事务一旦完成全部操作后,这个事务的结果必须是永久性的,不能因系统故障而从数据库中删除。

一个真正的 RDBMS 数据库系统将为每个事务保证所有的四个属性。使用 SQL 发布到数据库中的事务的简单视图如下:

使用 begin transaction 命令开始事务。

使用 SQL 查询语句执行各种删除、更新或插入操作。

如果所有的操作都成功,则执行提交操作,否则回滚所有操作。

Spring 框架在不同的底层事务管理 APIs 的顶部提供了一个抽象层。Spring 的事务支持旨在通过添加事务能力到 POJOs 来提供给 EJB 事务一个选择方案。Spring 支持编程式和声明式事务管理。EJBs 需要一个应用程序服务器,但 Spring 事务管理可以在不需要应用程序服务器的情况下实现,实现接口及实现类如下:

在这里插入图片描述

2.入门案例–模拟银行间转账业务

  1. 案例需求以及实现思路

在这里插入图片描述

  1. 环境准备
  • 创建数据库表tbl_account

建表查询语句示例

create database spring_db;
use spring_db;

drop table if exists tbl_account;

create table tbl_account(
	id int primary key auto_increment,
	name varchar(20),
	money varchar(20)
);

INSERT INTO tbl_account VALUES (1, 'Tom', 1000);
INSERT INTO tbl_account VALUES (2, 'Jerry', 1000);

效果

在这里插入图片描述

  • 在pom.xml文件中导入坐标
 <dependencies>
    //spring框架
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.10.RELEASE</version>
    </dependency>
    
    //druid数据源
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.16</version>
    </dependency>

    //mybatisa框架
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.6</version>
    </dependency>

    //数据库连接
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.47</version>
    </dependency>

    //spring操作jdbc
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.2.10.RELEASE</version>
    </dependency>

    //spring整合mybatis
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.3.0</version>
    </dependency>

    //spring整合junit单元测试
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
    
    //单元测试
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>5.2.10.RELEASE</version>
    </dependency>

  </dependencies>
  • 创建spring全局配置文件SpringConfig
 @Configuration
 @ComponentScan("com.itheima")
 @PropertySource("classpath:jdbc.properties")
 @Import({JdbcConfig.class,MybatisConfig.class})
 //开启注解式事务驱动
 @EnableTransactionManagement
 public class SpringConfig {
 }

  • 创建数据库信息文件jdbc.properties,填写数据库连接信息
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_db?useSSL=false
jdbc.username=root
jdbc.password=root
  • 创建数据库连接配置类JdbcConfig
public class JdbcConfig {
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String userName;
    @Value("${jdbc.password}")
    private String password;

    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(userName);
        ds.setPassword(password);
        return ds;
    }

    //配置事务管理器,mybatis使用的是jdbc事务
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource){
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }
}
  • 创建mybatis创建SqlSessionFactory Bean 和 Mapper 代理开发映射对象配置文件MybatisConfig
public class MybatisConfig {

    @Bean
    public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
        SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
        ssfb.setTypeAliasesPackage("org.example.domain");
        ssfb.setDataSource(dataSource);
        return ssfb;
    }

    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer(){
        MapperScannerConfigurer msc = new MapperScannerConfigurer();
        msc.setBasePackage("org.example.dao");
        return msc;
    }
}

  • 根据数据库表创建对应的实体类Account
public class Account{
    //此处省略getter、setter和toString方法
    private Integer id;
    private String name;
    private Double money;
    
}
  • 创建AccontDao接口,用注解的方式定义mapper代理开发业务接口,分别为加钱的操作方法 inmoney 和 减钱的操作 outmoney
public interface AccountDao {

    @Update("update tbl_account set money = money + #{money} where name = #{name}")
    void inMoney(@Param("name") String name, @Param("money") Double money);

    @Update("update tbl_account set money = money - #{money} where name = #{name}")
    void outMoney(@Param("name") String name, @Param("money") Double money);
}
  • 创建业务层接口AccountService,定义转账操作方法transfer
public interface AccountService {
    /**
     * 转账操作
     * @param out 传出方
     * @param in 转入方
     * @param money 金额
     */
    //配置当前接口方法具有事务
    @Transactional
    public void transfer(String out,String in ,Double money) ;
}

  • 创建业务层接口 AccountService 的实现类AccountServiceImpl,调用数据层功能的加钱和减钱方法 inmoney 和 outmoney
@Service
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountDao accountDao;

    public void transfer(String out,String in ,Double money) {
        //减钱
        accountDao.outMoney(out,money);
        //加钱
        accountDao.inMoney(in,money);
    }

}

  • 编写测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class AccountServiceTest {

    @Autowired
    private AccountService accountService;

    @Test
    public void testTransfer() throws IOException {
        //Tom 向 Jerry 转100元
        accountService.transfer("Tom","Jerry",100D);
    }

}
  • 程序运行结果

在这里插入图片描述
在这里插入图片描述

  • 文件结构参考

在这里插入图片描述

  1. 入门案例分析
  • 入门案例完成了简单的模拟转账操作,此时我们在转账接口的实现类AccountServiceImpl减钱和加钱的操作中间添加一行模拟业务运行异常的代码,此时我们希望业务异常,终止操作,即数据库的账户余额不变。

  • 添加了模拟异常的代码及运行结果
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述

  • 通过观察数据库中的表格数据我们发现异常前面的减钱操作运行了,而异常后面的加钱操作没有运行,导致整体业务功能失败。我们可以通过开启Spring事务来解决这个问题。
  1. 小结
  • Spring注解式事务通常添加在业务层接口中而不会添加到业务层实现类中,降低耦合
  • 注解式事务可以添加到业务方法上表示当前方法开启事务,也可以添加到接口上表示当前接口所有方法开启事务
  • 事务管理器需要根据实现技术进行选择,mybatis使用的是jdbc事务

3.开启Spring事务的一般步骤

  1. @Transactional 注解配置对应的接口方法具有事务
    @Transactional
    public void transfer(String out,String in ,Double money) ;
}

  1. 配置事务管理器,mybatis使用的是jdbc事务
public class JdbcConfig {
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource){
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }
}

  1. @EnableTransactionManagement 注解开启注解式事务驱动
@EnableTransactionManagement
public class SpringConfig {

}

此时在存在模拟异常的情况下再次运行模拟测试类,数据库中的数据不变,业务功能运行正常,Spring事务开启成功。

4.Spring事务角色

  1. Spring事务角色分为事务管理员和事务协调员
  • 事务管理员:发起事务方,在Spring中通常指代业务层开启事务的方法

  • 事务协调员:加入事务方,在Spring中通常指代数据层方法,也可以是业务层方法

  1. 结合入门案例分析

在这里插入图片描述

  • 在入门案例中,没加@Transactional注解开启事务的情况下,减钱的outmoney方法、加钱的方法inmoney以及transfer接口都会各自开启一个事务,各自独立。当运行加钱事务出现异常的时候,减钱操作并不会加入加钱操作的事务回滚中,导致整个业务运行失败。

  • 当只用@Transactional注解开启事务后,事务减钱事务TI与加钱事务T2都加入事务T,成为一个事务,这样当事务中的加钱事务部分出现问题时,整个事务回滚,程序终止运行,整体业务正常运行成功。

5.spring事务属性–rollbackfor

  1. 属性简介
属性作用示例
readonly设置是否为只读事务readOnly=true 只读事务
timeout设置事务超时时间timeout=-1(永不超时)
rollbackfor设置事务回滚异常(class){NullPointException.class rollbackFor=
rollbackForClassName设置事务回滚异常(string)同上格式为字符串
noRollbackFor设置事务不回滚异常(class)noRollbackFor= (NullPointException.class}
noRollbackForClassName设置事务不回滚异常(string)同上格式为字符串
propagation设置事务传播行为
  1. 添加事务属性格式举例
public interface AccountService {
    /**
     * 转账操作
     * @param out 传出方
     * @param in 转入方
     * @param money 金额
     */
    //配置当前接口方法具有事务
    @Transactional(readOnly = true,timeout = -1)
    public void transfer(String out,String in ,Double money) ;
}
  1. rollbackfor 属性介绍
  • 在业务层实现类中抛出一个IOException异常
@Service
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;

    public void transfer(String out,String in ,Double money) throws IOException {
        accountDao.outMoney(out,money);
        //模拟抛出IOException异常
        if(true){throw new IOException();}
        accountDao.inMoney(in,money);
    }

}

  • 运行模拟测试类

在这里插入图片描述
在这里插入图片描述

  • 观察数据库表我们发现,开启事务后在遇到抛IOException类型的异常时,事务不会主动回滚,导致整体业务失败,这时候需要主动去配置。
public interface AccountService {
    /**
     * 转账操作
     * @param out 传出方
     * @param in 转入方
     * @param money 金额
     */
    //配置当前接口方法具有事务
    @Transactional(rollbackFor = {IOException.class})
    public void transfer(String out,String in ,Double money) throws IOException;
}

此时再次运行模拟测试类,当检测到IOExctpition类型的异常时,事务T回滚,数据库表中的数据不变。

  • 开启Spring事务只回滚两种事务:ERROR系列(如内存溢出) 和 运行时异常(如NO Point Exception)。其余的均需要通过rollbackfor属性手动配置。

6.入门案例进阶–转账业务追加日志

  1. 案例需求及实现思路

在这里插入图片描述

  1. 编码环境准备
  • 创建数据库表tbl_log

sql建表语句

use spring_db;

drop table if exists tbl_log;

create table tbl_log(
	id int primary key auto_increment,
	info varchar(100),
	createDate varchar(20)
);

INSERT INTO tbl_log VALUES (1, '转账操作由Tom到Jerry,金额:100.0', '20:55:49');

表格示例

在这里插入图片描述

  • LogDao数据层接口
public interface LogDao {
    @Insert("insert into tbl_log (info,createDate) values(#{info},now())")
    void log(String info);
}

  • LogService 业务层接口
public interface LogService {
    @Transactional
    void log(String out, String in, Double money);
}

  • LogService 业务层接口实现类
@Service
public class LogServiceImpl implements LogService {

    @Autowired
    private LogDao logDao;

    public void log(String out,String in,Double money ) {
        logDao.log("转账操作由"+out+"到"+in+",金额:"+money);
    }
}
  • 将IOException相关的代码注释掉,保留最基础的Spring事务代码

  • 文件结构示例

在这里插入图片描述

  1. 用try…finally结构编写业务层实现类AccountServiceImpl 并运行测试类
@Service
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;

    @Autowired
    private LogService logService;

    public void transfer(String out,String in ,Double money) {
        try{
            accountDao.outMoney(out,money);
            int i = 1/0;
            accountDao.inMoney(in,money);
        }finally {
            logService.log(out,in,money);
        }
    }

}

  • 将数据库中的金额分别手动修改重置到1000,并执行测试类

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

7. Spring事务属性–事务传播行为

  1. 探讨添加了异常后,日志能否正常加载

在这里插入图片描述

  • 运行结果分析

此时再次运行测试类,我们可以发现数据库表中金额表数值由于开启了事务,当出现异常时,事务回滚,金额数值不变。同时日志表的数据也没有发生变化,不符合我们的预期。

  • 存在的问题:

日志的记录与转账操作隶属同一个事务,同成功同失败

在这里插入图片描述

  • 实现效果预期改进:

无论转账操作是否成功,日志必须保留,这时就需要通过事务传播行为来解决这个问题

在这里插入图片描述

  1. spring事务的7种传播行为
required(默认传播行为),如果当前有事务,其他就用当前事务,不会新增事务。例如:方法A调用方法B,它们用同一个事务。(如果B没有事务,它们会用同一个事务。)(只要有一个回滚,整体就会回滚)
requires_new如果当前有事务,其他不会加入当前事务,会新增事务。即他们的事务没有关系,不是同一个事务。如果其他没有事务,那么以当前事务运行。例如:方法A调用方法B,它们用不同的事务。(B不会用A的事务,会新增事务)
supports当前没有事务,就以非事务运行。当前有事务呢?就以当前事务运行。例如:方法A调用方法B,如果A没有事务,那么B就以非事务运行。如果A有事务就以A事务为准。如果A没有事务,那么B就会以非事务执行。
mandatory其他没有事务就会抛异常。当前没有事务抛出异常,当前有事务则支持当前事务。支持当前事务,如果当前没有事务就会抛出异常。例如:方法A调用方法B,如果方法A没有事务,那么就会抛出异常。
not_supported以非事务执行。例如:方法A调用方法B,方法B会挂起事务A以非事务方式执行。
never以非事务执行,如果存在事务,抛出异常。总是以非事务执行,如果存在事务,那么就抛出异常。
nested如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为与Required一样。例如:方法A中调用了方法B,B中try catch手动回滚,A不会回滚。
  1. 设置日志接口方法的事务传播行为属性
public interface LogService {
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    void log(String out, String in, Double money);
}
  1. 运行测试类观察效果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
基于以上结果,Spring事务传播行为设置成功,业务功能运行正常。

1.【w3cschool】
2. 【spring教学视频】

总结

欢迎各位留言交流以及批评指正,如果文章对您有帮助或者觉得作者写的还不错可以点一下关注,点赞,收藏支持一下作者,后续还会更新mybatis, springboot,maven高级,微信小程序,等前后端内容的学习笔记。
(博客的参考源码可以在我主页的资源里找到,如果在学习的过程中有什么疑问欢迎大家在评论区向我提出)

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

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

相关文章

项目管理:有效的沟通对项目的成功至关重要

为实施有效的沟通&#xff0c;需要建立沟通管理计划同时理解什么是沟通&#xff0c;沟通的对象是谁&#xff0c;沟通的目标是什么&#xff0c;难度在哪里&#xff0c;并选择合适的沟通方式。 项目沟通是确保项目团队的相关信息能及时、正确地产生、收集、发布、储存和最终处理…

77.建立一个Web应用程序的布局第一部分

本次我们需要设计的布局是这样样子&#xff0c;这个很想一个邮件系统的基本布局&#xff1b; ● 首先我们生成基础代码&#xff0c;基础代码很简单&#xff0c;不用过多解释 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-…

MySQL锁应用详解

文章目录 前言MySQL锁的详解1. 表级锁1.1 读锁&#xff08;共享锁&#xff09;对比查询操作更新操作获取写锁获取读锁 1.2 写锁&#xff08;排他锁&#xff09;对比获取写锁对表进行事务操作获取表的读锁对表进行查询操作 2. 行级锁2.1 共享锁2.2 排他锁 锁的应用场景1.1 并发读…

PDF怎么添加水印?简单途径说明

在工作中&#xff0c;我们经常需要对PDF文档进行保护&#xff0c;以确保其不被未经授权的人员查看或修改。其中一种常见的保护方式是在PDF文件中添加水印。水印不仅可以保护文件的安全性&#xff0c;还可以帮助识别文档的来源以及保护版权。在本文中&#xff0c;我们将介绍如何…

如何邀请媒体记者,保证新闻发布会媒体邀约?

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 要邀请媒体记者参加新闻发布会并确保他们能够接受邀请&#xff0c;可以按照以下步骤进行&#xff1a; 1.制定计划&#xff1a;确定新闻发布会的日期、时间、地点和主题。确保选择一个方…

【TCP】对TCP三次握手的个人理解

三次握手 TCP 是面向连接的协议&#xff0c;所以使用 TCP 前必须先建立连接&#xff0c;而建立连接是通过三次握手来进行的。三次握手的过程如下图&#xff1a; 一开始&#xff0c;客户端和服务端都处于 CLOSE 状态。先是服务端主动监听某个端口&#xff0c;处于 LISTEN 状态 …

功能上新|内存篇:PSS显存、内存占用、堆内存对象快照

内存管理一直是游戏研发的重中之重&#xff0c;当项目运行时的内存压力较大时&#xff0c;更容易达到设备阈值引起闪退。近年来&#xff0c;当出海成为许多游戏公司新选择的同时&#xff0c;我们也发现海外设备对项目的内存情况有着更严格的要求。 为了帮助开发者更全面地了解…

从内核角度剖析Netty高性能的奥秘IO多路复用模型与Reactor模式

Netty 是Jboos 提供的java开源框架&#xff0c; 是基于非阻塞IO&#xff08;NIO&#xff09;的客户端/服务器编程框架&#xff0c; 它既能快速开发高并发、高可用、高可靠的网络服务器程序&#xff0c;也能开发高可用、高可靠的客户端程序。 Netty 作为异步框架&#xff0c; N…

面试:从输入URL到页面加载的全过程

首先在浏览器中输入URL 查找缓存&#xff1a;浏览器先查看浏览器缓存-系统缓存-路由缓存中是否有该地址页面&#xff0c;如果有则显示页面内容。如果没有则进行下一步。 浏览器缓存&#xff1a;浏览器会记录DNS一段时间&#xff0c;因此&#xff0c;只是第一个地方解析DNS请求…

【算法排序】动态规划

目录 一、动态规划思想二、动态规划与分治法的区别1、共同点2、不同点 三、动态规划特征1、最优子结构2、重叠子问题 四、动态规划求解问题的基本步骤五、斐波那契数分析六、实现思路七、代码实现 一、动态规划思想 将待求问题划分为若干个子问题&#xff0c;按划分的顺序求解…

为什么袁隆平的英语这么好?这才是学到老的典范!

文 / 冰雪&#xff08;微信公众号&#xff1a;王不留&#xff09; 2021年5月22日13时07分&#xff0c;“共和国勋章”获得者、中国工程院院士、国家杂交水稻工程技术研究中心主任、湖南省政协原副主席袁隆平&#xff0c;因病逝世&#xff0c;享年91岁。 一晃两年过去了。袁隆平…

iTOP-RK3568开发板编译瑞芯微原厂源码

1 输入以下命令设置 java 版本为 1.8 版本&#xff0c;确认 java 版本是 1.8 版本之后&#xff0c;才可以进行下一步编译&#xff0c;如下图所示&#xff1a; source javaenv.sh java -version 2 输入命令配置 Android 分支 source build/envsetup.sh lunch rk3568_r-user…

【软考】系统集成项目管理工程师 第3章 信息系统集成专业技术知识

文章目录 3.1 信息系统建设3.1.1 信息系统的生命周期3.1.2信息系统开发方法 3.3 软件工程3.3.1软件需求分析与定义3.3.2软件设计、测试与维护3.3.3软件质量保证及质量评价3.3.4软件配置管理3.3.5软件过程管理3.3.6软件开发工具3.3.7软件复用 3.4 面向对象系统分析与设计3.4.1面…

ESP32-WROOM-32 TCP通讯AT指令例程

ESP32-WROOM-32 AT指令配置TCP通讯 ESP32-WROOM-32前言固件烧录测试AT指令TCP通讯\透传ESP32配置SoftAPESP32作TCP Client连接TCP Server通讯/透传普通传输模式演示WIFI透传演示 ESP32做TCP Server连接TCP Client通讯演示 ESP32-WROOM-32 前言 软硬件准备&#xff1a; ESP32-W…

自动驾驶行业迎来大规模发展,“高精度定位”重要性凸显

5月16日&#xff0c;2023 中国 (亦庄) 智能网联汽车科技周暨第十届国际智能网联汽车技术年会在北京隆重召开。工信部装备工业一司一级巡视员苗长兴在在会上表示&#xff1a;2022 年我国搭载辅助自动驾驶系统的智能网联乘用车新车销售量达 700 万辆&#xff0c;同比增长45.6%&am…

计算机操作系统(慕课版)第三章课后题答案

一、简答题 1.高级调度与低级调度的主要任务是什么&#xff1f;为什么要引入中级调度&#xff1f; 1&#xff09;高级调度的主要任务是将外存的作业调入内存&#xff0c;又称作业调度&#xff1b; 低级调度的主要任务数为内存中处于就绪态的作业分配处理机。 2&#xff09;为了…

小航助学2023年3月GESP_C++二级试卷(含题库答题软件账号)

GESP在线模拟训练系统请点击 电子学会-全国青少年编程等级考试真题Scratch一级&#xff08;2019年3月&#xff09;在线答题_程序猿下山的博客-CSDN博客_小航答题助手 答案:D 第1题以下存储器中的数据不会受到附近强磁场干扰的是&#xff08; &#xff09;。 A、硬盘B、U 盘C…

共享单车之数据存储-获取工作簿中的数据

第1关&#xff1a;获取工作簿中的数据 任务描述 本关任务&#xff1a;获取data.xls文件中的数据。 相关知识 获取工作簿中的信息&#xff0c;我们可以使用Java POI&#xff08;POI是一个提供API给Java程序对Microsoft Office格式档案读和写的功能&#xff09;提供的Workboo…

二十三种设计模式第七篇--适配器模式

适配器模式 适配器模式&#xff08;Adapter Pattern&#xff09;是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于 结构型模式&#xff0c;它结合了两个独立接口的功能。这种模式涉及到一个单一的类&#xff0c;该类负责加入独立的或不兼容的接口功能。 我们生活中…

注意!ChatGPT的Plus账号也会被封禁

文 / 高扬&#xff08;微信公众号&#xff1a;量子论&#xff09; 最近经常看到有人丢个截图&#xff0c;然后悲伤地说&#xff0c;ChatGPT账号被封了。 这不是账号被封&#xff0c;而是所用的代理节点被OpenAI封了&#xff0c;换个节点即可。 这个截图才是账号真正被封的提示&…