Mybatis延迟加载和查询缓存

Mybatis延迟加载和查询缓存Mybatis延迟加载和查询缓存

大家好,又见面了,我是你们的朋友全栈君。

一、延迟加载

resultMap可以实现高级映射(使用association、collection实现一对一及一对多映射),association、collection具备延迟加载功能。
  延迟加载:先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。

在mybatis核心配置文件中配置:
lazyLoadingEnabled、aggressiveLazyLoading

设置项 描述 允许值 默认值
lazyLoadingEnabled 全局性设置懒加载。如果设为‘false’,则所有相关联的都会被初始化加载。 true \ false false
aggressiveLazyLoading 当设置为‘true’的时候,懒加载的对象可能被任何懒属性全部加载。否则,每个属性都按需加载。 true \ false true
//例如
<settings>
      <setting name="lazyLoadingEnabled" value="true"/>
      <setting name="aggressiveLazyLoading" value="false"/>
</settings>

场合:
当只有部分记录需要关联查询其它信息时,此时可按需延迟加载,需要关联查询时再向数据库发出sql,以提高数据库性能。
当全部需要关联查询信息时,此时不用延迟加载,直接将关联查询信息全部返回即可,可使用resultType或resultMap完成映射。

二、查询缓存

Mybatis提供查询缓存,用于减轻数据压力,提高数据库压力。
  Mybatis提供一级缓存和二级缓存。
   在这里插入图片描述
  在操作数据库时需要构造SqlSession对象,在对象中有一个数据结构(HashMap)用于缓存数据。
  不同的SqlSession之间的缓存数据区域是互相不影响的。
  Mybatis一级缓存的作用域是同一个SqlSession,在同一个sqlSession中两次执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。当一个sqlSession结束后该sqlSession中的一级缓存也就不存在了。Mybatis默认开启一级缓存。
  Mybatis二级缓存是多个SqlSession共享的,其作用域是mapper的同一个namespace,不同的sqlSession两次执行相同namespace下的sql语句且向sql中传递参数也相同即最终执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。Mybatis默认没有开启二级缓存需要在setting全局参数中配置开启二级缓存。

1、一级缓存

在这里插入图片描述
  第一次发起查询,先去找缓存中是否有id为1的用户信息,如果没有,从数据库中查询用户信息。
  得到用户信息,将用户信息存储到一级缓存中。
  第二次发起查询用户id为1的用户信息,先去缓存中是否有id为1的用户信息,缓存中有,直接从缓存中获取用户信息。
  如果SqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存。目的是为了让缓存中存储的是最新的信息,避免脏读。
  Mybatis默认支持一级缓存,不需要在配置文件中配置。
  Mybatis内部存储缓存使用一个HashMap,key为hashCode+sqlId+Sql语句。value为从查询出来映射生成的java对象。
  应用场景:
  在这里插入图片描述

2、二级缓存

在这里插入图片描述
  SqlSession1去查询用户id为1的用户信息,查询到用户信息会将查询数据存储到二级缓存中。
  SqlSession2去查询用户id为1的用户信息,去缓存中找是否存在数据,如果存在直接从缓存中取出数据。
  二级缓存区域是根据mapper的namespace划分的,相同namespace的mapper查询数据放在同一个区域,如果使用mapper代理方法每个mapper的namespace都不同,此时可以理解为二级缓存区域是根据mapper划分。
每次查询会先从缓存区域找,如果找不到从数据库查询,查询到数据将数据写入缓存。
  Mybatis内部存储缓存使用一个HashMap,key为hashCode+sqlId+Sql语句。value为从查询出来映射生成的java对象
  sqlSession执行insert、update、delete等操作commit提交后会清空缓存区域。
  开启二级缓存:
  在核心配置文件SqlMapConfig.xml中加入

    <setting name="cacheEnabled" value="true"/>
描述 允许值 默认值
cacheEnabled对在此配置文件下的所有cache 进行全局性开/关设置。 true false true

要在你的Mapper映射文件中添加一行: ,表示此mapper

开启二级缓存。

二级缓存需要查询结果映射的pojo对象实现java.io.Serializable接口实现序列化和反序列化操作,注意如果存在父类、成员pojo都需要实现序列化接口。
  为了将缓存数据取出执行反序列化,因为二级缓存存储介质多种多样,不一定在内存。

禁用二级缓存:

在statement中设置useCache=false可以禁用当前select语句的二级缓存,即每次查询都会发出sql去查询,默认情况是true,即该sql使用二级缓存。

<select id="findOrderListResultMap" resultMap="ordersUserMap" useCache="false">

刷新缓存(就是清空缓存):
  设置statement配置中的flushCache=“true” 属性,默认情况下为true即刷新缓存,如果改成false则不会刷新。使用缓存时如果手动修改数据库表中的查询数据会出现脏读。

<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User" flushCache="true">

应用场景:
  对于访问多的查询请求且用户对查询结果实时性要求不高,此时可采用mybatis二级缓存技术降低数据库访问量,提高访问速度,业务场景比如:耗时较高的统计分析sql、电话账单查询sql等。
实现方法如下:通过设置刷新间隔时间,由mybatis每隔一段时间自动清空缓存,根据数据变化频率设置缓存刷新间隔flushInterval,比如设置为30分钟、60分钟、24小时等,根据需求而定。
  局限性:
  mybatis二级缓存对细粒度的数据级别的缓存实现不好,比如如下需求:对商品信息进行缓存,由于商品信息查询访问量大,但是要求用户每次都能查询最新的商品信息,此时如果使用mybatis的二级缓存就无法实现当一个商品变化时只刷新该商品的缓存信息而不刷新其它商品的信息,因为mybaits的二级缓存区域以mapper为单位划分,当一个商品信息变化会将所有商品信息的缓存数据全部清空。解决此类问题需要在业务层根据需求对数据有针对性缓存。

案例

在这里插入图片描述
UserMapper.java

package com.xbj.mapper;
import com.xbj.po.TUser;
import java.util.List;
/** * 延迟加载 */
public interface UserMapper { 
   
    //延迟加载
    List<TUser> userIncludeOrder();
    TUser findUserById(Integer id);
    List<TUser> findAll();
    void deleteById(Integer id);
}

UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xbj.mapper.UserMapper">
<!--开启二级缓存-->
<cache />
<!--SQL片段的引用-->
<sql id="baseSql">
SELECT * from t_user
</sql>
<!--延迟加载-->
<resultMap id="userInfos" type="TUser" autoMapping="true">
<id property="id" column="id"/>
<collection property="tOrders" ofType="TOrder" select="findorderByUid" column="id"/>
</resultMap>
<select id="findorderByUid" resultType="TOrder" parameterType="int">
SELECT  * from t_order where uid=#{id}
</select>
<select id="userIncludeOrder" resultMap="userInfos">
SELECT * from t_user
</select>
<select id="findUserById" parameterType="int" resultType="TUser">
SELECT * FROM t_user where id=#{id}
</select>
<select id="findAll" resultType="TUser">
SELECT * from t_user
</select>
<delete id="deleteById" parameterType="int" >
DELETE  from t_user WHERE id=#{id}
</delete>
</mapper>

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>groupId</groupId>
<artifactId>dome02</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
</dependency>
</dependencies>
</project>

sqlMapConfig.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<!--允许延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
<typeAliases>
<package name="com.xbj.po" />
</typeAliases>
<!--数据源的配置-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/user"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!--对象的操作-->
<mappers>
<!--<mapper resource="com/com.xbj/mapper/UserMapper.xml"/>-->
<!--<mapper url="file:///D:\projects\javaprojects\620\Demo02\src\main\resources\com\com.xbj\mapper\UserMapper.xml" />-->
<!--<mapper class="com.com.xbj.mapper.UserMapper" />-->
<package name="com.xbj.mapper" />
</mappers>
</configuration>

log4j.properties

# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# MyBatis logging configuration...
log4j.logger.org.mybatis.example.BlogMapper=TRACE
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

TOrder.java

package com.xbj.po;
import java.io.Serializable;
import java.util.List;
/**
* @Author:晓宇码匠
* @Date:2019/6/21 0021
*/
public class TUser implements Serializable {
private int id;
private String username;
private String password;
private Integer age;
private List<TOrder> tOrders;
public List<TOrder> gettOrders() {
return tOrders;
}
public void settOrders(List<TOrder> tOrders) {
this.tOrders = tOrders;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TUser tUser = (TUser) o;
if (id != tUser.id) return false;
if (username != null ? !username.equals(tUser.username) : tUser.username != null) return false;
if (password != null ? !password.equals(tUser.password) : tUser.password != null) return false;
if (age != null ? !age.equals(tUser.age) : tUser.age != null) return false;
return true;
}
@Override
public int hashCode() {
int result = id;
result = 31 * result + (username != null ? username.hashCode() : 0);
result = 31 * result + (password != null ? password.hashCode() : 0);
result = 31 * result + (age != null ? age.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "TUser{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", age=" + age +
'}';
}
}

TUser.java

package com.xbj.po;
import java.io.Serializable;
import java.util.List;
/**
* @Author:晓宇码匠
* @Date:2019/6/21 0021
*/
public class TUser implements Serializable {
private int id;
private String username;
private String password;
private Integer age;
private List<TOrder> tOrders;
public List<TOrder> gettOrders() {
return tOrders;
}
public void settOrders(List<TOrder> tOrders) {
this.tOrders = tOrders;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TUser tUser = (TUser) o;
if (id != tUser.id) return false;
if (username != null ? !username.equals(tUser.username) : tUser.username != null) return false;
if (password != null ? !password.equals(tUser.password) : tUser.password != null) return false;
if (age != null ? !age.equals(tUser.age) : tUser.age != null) return false;
return true;
}
@Override
public int hashCode() {
int result = id;
result = 31 * result + (username != null ? username.hashCode() : 0);
result = 31 * result + (password != null ? password.hashCode() : 0);
result = 31 * result + (age != null ? age.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "TUser{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", age=" + age +
'}';
}
}

test.java

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Before;
import org.junit.Test;
import com.xbj.mapper.UserMapper;
import com.xbj.po.TUser;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
/**
* @Author:晓宇码匠
* @Date:2019/6/21 0021
*/
public class test {
private UserMapper userMapper;
@Before
public void init() throws IOException {
InputStream is= Resources.getResourceAsStream("sqlMapConfig.xml");
userMapper=new SqlSessionFactoryBuilder().build(is).openSession(true).getMapper(UserMapper.class);
}
//测试延迟加载
@Test
public void t1(){
List<TUser> infos=userMapper.userIncludeOrder();
System.out.println(infos);
}
//测试一级缓存
@Test
public void t2(){
TUser user1=userMapper.findUserById(1);
TUser user2=userMapper.findUserById(1);
}
/*在进行增、改、删操作的时候会清空缓存*/
@Test
public void t3(){
userMapper.findAll();
userMapper.deleteById(4);
userMapper.findAll();
}
}

Test02.java

import com.xbj.mapper.UserMapper;
import com.xbj.po.TUser;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
public class Test02 {
private SqlSessionFactory factory;
private UserMapper userMapper;
private UserMapper userMapper2;
@Before
public void init() throws IOException {
InputStream is= Resources.getResourceAsStream("sqlMapConfig.xml");
factory=new SqlSessionFactoryBuilder().build(is);
userMapper=factory.openSession(true).getMapper(UserMapper.class);
userMapper2=factory.openSession(true).getMapper(UserMapper.class);
}
/*测试在不同的mapper会话中,是访问不同的缓存区*/
@Test
public void t1(){
TUser user1=userMapper.findUserById(1);
TUser user2=userMapper2.findUserById(1);
}
/*在进行增、改、删操作的时候会清空缓存*/
@Test
public void t2(){
userMapper.findAll();
userMapper.deleteById(4);
userMapper.findAll();
}
/*测试二级缓存的时候,需要将第一个查询操作先关闭,否则程序不会进入缓存里面查找数据*/
@Test
public void t3(){
SqlSession sqlSession1=factory.openSession(true);
SqlSession sqlSession2=factory.openSession(true);
sqlSession1.selectOne("findUserById",1);
sqlSession1.close();  //第一个人 调用后 会话结束 写入缓存
sqlSession2.selectOne("findUserById",1);
}
}

sql.sql

/*
Navicat MySQL Data Transfer
Source Server         : Mysql
Source Server Version : 50717
Source Host           : localhost:3306
Source Database       : user
Target Server Type    : MYSQL
Target Server Version : 50717
File Encoding         : 65001
Date: 2019-06-21 15:06:09
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for t_item
-- ----------------------------
DROP TABLE IF EXISTS `t_item`;
CREATE TABLE `t_item` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(200) NOT NULL,
`detail` varchar(200) DEFAULT NULL,
`price` float(10,2) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of t_item
-- ----------------------------
INSERT INTO `t_item` VALUES ('1', '苹果', '真好吃', '6.00');
INSERT INTO `t_item` VALUES ('2', '橘子', '淮南橘子', '3.00');
INSERT INTO `t_item` VALUES ('3', '香蕉', '海南香蕉,香蕉中的战斗蕉', '2.00');
INSERT INTO `t_item` VALUES ('4', '芒果', '泰国芒果', '11.00');
-- ----------------------------
-- Table structure for t_order
-- ----------------------------
DROP TABLE IF EXISTS `t_order`;
CREATE TABLE `t_order` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`uid` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `uid` (`uid`),
CONSTRAINT `t_order_ibfk_1` FOREIGN KEY (`uid`) REFERENCES `t_user` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of t_order
-- ----------------------------
INSERT INTO `t_order` VALUES ('1', '1');
INSERT INTO `t_order` VALUES ('2', '1');
INSERT INTO `t_order` VALUES ('3', '1');
INSERT INTO `t_order` VALUES ('4', '2');
INSERT INTO `t_order` VALUES ('5', '3');
-- ----------------------------
-- Table structure for t_order_detail
-- ----------------------------
DROP TABLE IF EXISTS `t_order_detail`;
CREATE TABLE `t_order_detail` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`oid` int(11) DEFAULT NULL,
`item_id` int(11) DEFAULT NULL,
`count` float(8,2) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `oid` (`oid`),
KEY `item_id` (`item_id`),
CONSTRAINT `t_order_detail_ibfk_1` FOREIGN KEY (`oid`) REFERENCES `t_order` (`id`),
CONSTRAINT `t_order_detail_ibfk_2` FOREIGN KEY (`item_id`) REFERENCES `t_item` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of t_order_detail
-- ----------------------------
INSERT INTO `t_order_detail` VALUES ('1', '1', '1', '2.00');
INSERT INTO `t_order_detail` VALUES ('2', '1', '2', '2.00');
INSERT INTO `t_order_detail` VALUES ('3', '2', '3', '2.00');
INSERT INTO `t_order_detail` VALUES ('4', '2', '4', '2.00');
INSERT INTO `t_order_detail` VALUES ('5', '3', '1', '2.00');
INSERT INTO `t_order_detail` VALUES ('6', '3', '1', '2.00');
INSERT INTO `t_order_detail` VALUES ('7', '4', '2', '2.00');
INSERT INTO `t_order_detail` VALUES ('8', '4', '2', '2.00');
INSERT INTO `t_order_detail` VALUES ('9', '5', '1', '2.00');
INSERT INTO `t_order_detail` VALUES ('10', '1', '4', '2.00');
-- ----------------------------
-- Table structure for t_user
-- ----------------------------
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(200) NOT NULL,
`password` varchar(200) NOT NULL,
`age` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of t_user
-- ----------------------------
INSERT INTO `t_user` VALUES ('1', 'tom', 'ok', '33');
INSERT INTO `t_user` VALUES ('2', 'lucy', 'ok', '43');
INSERT INTO `t_user` VALUES ('3', 'jack', 'ok', '53');
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/106618.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • 正则匹配数组全部是数字[通俗易懂]

    正则匹配数组全部是数字[通俗易懂]var value=[“2″,”3”]   varv=value.join(“”);varreg=/^\d+$/g;if(!v.match(reg)){//是否匹配到了数字,未匹配到就不是return;}if(v.match(reg)[0].lengthreturn;}

  • telnet ip 端口 命令作用_telnet怎么查看端口状态

    telnet ip 端口 命令作用_telnet怎么查看端口状态telnetip端口命令的打开方法:首先点击开始按钮,并打开控制面板;然后在“查看方法”为类别时单击“程序”;接着单击“程序和功能”下的“打开或关闭Windows功能”;最后点击“Telnet客户端”即可。telnetip端口命令的打开方法:1、首先单击左下角的“开始”按钮以打开“开始”菜单,然后打开“控制面板”2、进入控制面板后,在“查看方法”为类别时单击“程序”3、打开“程序”页面后,单击“程序和功能”下的“打开或关闭Windows功能”4、之后您将进入“W.

  • 我的世界java版需要多少钱_我的世界Java版20w49a快照版[通俗易懂]

    我的世界java版需要多少钱_我的世界Java版20w49a快照版[通俗易懂]我的世界Java版20w49a快照版游戏是我的世界最新版本游戏,更新了许多新颖独特的元素,超大的地图世界可以自由探索,全新的故事情节完美融入其中,各种各样的玩法让你无限制的去毛线,全新的世界带给你不一样的欢乐。喜欢我的世界的玩家不要错过哦!我的世界Java版20w49a快照版游戏简介1、玩家可以探索去寻找一些稀有的水晶,这些水晶的种类有很多,收集这些资源即可让你建造出更多有意思的内容;2、全新的家…

  • java的rsa加密算法_用java编程实现RSA加密算法

    java的rsa加密算法_用java编程实现RSA加密算法RSA加密算法是目前应用最广泛的公钥加密算法,特别适用于通过Internet传送的数据,常用于数字签名和密钥交换。那么我今天就给大家介绍一下如何利用Java编程来实现RSA加密算法。一、RSA加密算法描述RSA加密算法是1978年提出的。经过多年的分析和研究,在众多的公开密钥加密算法中,RSA加密算法最受推崇,它也被推荐为公开密钥数据加密标准。由数论知识可知,若将一个具有大素数因子的合数进行分解是…

  • 硬盘恢复分区_怎么把efi分区删掉

    硬盘恢复分区_怎么把efi分区删掉Windows系统在安装的时候,会自动为我们的磁盘划分一个恢复分区和一个EFI分区。如果后面不打算再用这些分区的时候,却发现无法删除。本文将提供解决方法。因为误操作会导致数据丢失,所以我将两种不同的解决方法分开成两篇文章以避免干扰:EFI分区/恢复分区不可删除?你需要使用命令行了(配合鼠标操作)EFI分区/恢复分区不可删除?你需要使用命令行了(全命令行操作)本文内容无法删…

  • stm32 spi协议_STM32库开发实战指南:基于STM32F4

    stm32 spi协议_STM32库开发实战指南:基于STM32F4深入讲解SPI协议通信时序,详细解析SPI读写串行FALSH实验,NorFlash的存储特性以及读写指令的详细介绍!!!

    2022年10月15日

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号