当前位置: 首页 > news >正文

橘子学Mybatis08之Mybatis关于一级缓存的使用和适配器设计模式

前面我们说了mybatis的缓存设计体系,这里我们来正式看一下这玩意到底是咋个用法。
首先我们是知道的,Mybatis中存在两级缓存。分别是一级缓存(会话级),和二级缓存(全局级)。
下面我们就来看看这两级缓存。

一、准备工作

1、准备数据库

在此之前我们先来准备一个数据库,并且设计一个表user用户表,加几条数据进去。

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (`id` int(32) NOT NULL COMMENT '主键id',`name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '名字',PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户表' ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, '张飞');
INSERT INTO `user` VALUES (2, '关羽');
INSERT INTO `user` VALUES (3, '孙权');SET FOREIGN_KEY_CHECKS = 1;

2、然后我们创建一个mybatis中的DAO接口。

public interface IUserDao {// 查询所有用户List<User> findAll();
}

3、创建UserMapper.xml文件

<mapper namespace="com.yx.dao.IUserDao"><select id="findAll" resultType="com.yx.domain.User" statementType="CALLABLE">SELECT * FROM `user`</select
</mapper>

4、创建Mybatis核心配置文件

<configuration><!--加载外部properties文件,位置必须在第一个--><properties resource="jdbc.properties"/><settings><!-- 控制台输出sql语句 --><setting name="logImpl" value="STDOUT_LOGGING" /></settings><typeAliases><!--给单独的实体类取别名--><!--<typeAlias type="com.lwq.domain.User" alias="user"/>--><!--批量给实体类取别名:指定包下面所有的类的别名默认为其类名,不区分大小写--><package name="com.yx.domain"/></typeAliases><environments default="development"><!--开发环境,可以配置多套环境,default指定用哪个--><environment id="development"><!--當前事務交給jdbc處理--><transactionManager type="JDBC"/><dataSource type="POOLED"><property name="driver" value="${jdbc.driver}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></dataSource></environment></environments><mappers><mapper resource="UserMapper.xml"/></mappers>
</configuration>

5、创建jdbc.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///mybatis22
jdbc.username=root
jdbc.password=root

好了,我们现在可以使用mybatis来操作数据库了。

二、一级缓存

1、验证

一级缓存在mybatis中是默认开启的,我们先来测试一下,他到底存在不存在,并且是不是能用,是不是默认开启的。我们来做一个测试代码。

@Test
public void test1() throws IOException {// 读取配置文件转化为流InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);SqlSession sqlSession = factory.openSession();//这⾥不调⽤SqlSession的api,⽽是获得了接⼝对象,调⽤接⼝中的⽅法。使用JDK动态代理产生代理IUserDao userDao = sqlSession.getMapper(IUserDao.class);// 第一次执行查询List<User> userList = userDao.findAll();userList.forEach(user -> {System.out.println(user.toString());});System.out.println("*************************************************");// 第二次执行相同的查询List<User> userList2 = userDao.findAll();userList2.forEach(user -> {System.out.println(user.toString());});
}这里我们做了两次查询,第一次和第二次的查询是一模一样的,如果存在缓存,那它一定第二次就不会去查数据库
了,下面我们来验证一下。

输出如下:

Opening JDBC Connection
Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
Created connection 1011044643.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@3c435123]
==>  Preparing: SELECT * FROM `user`
==> Parameters: 
<==    Columns: id, name
<==        Row: 1, 张飞
<==        Row: 2, 关羽
<==        Row: 3, 孙权
<==      Total: 3
User{id=1, username='张飞'}
User{id=2, username='关羽'}
User{id=3, username='孙权'}
*************************************************
User{id=1, username='张飞'}
User{id=2, username='关羽'}
User{id=3, username='孙权'}

我们看到第一次调用进行了创建connection连接,下面执行了sql,然后输出,但是第二次再执行,他就不会再去查库了,而是直接输出了缓存,这就说明了这个缓存是存在并且开启的。

2、注意

2.1、注意点1

一级缓存只在当前sqlsession中生效,也就是他是会话级别的,每一次连接内的多次相同查询可以共享这次缓存,如果你换了sqlsession,新的sqlsession和其他的sqlsession的查询是不能共享缓存使用的。下面我们来验证一下。

@Test
public void test2() throws IOException {// 读取配置文件转化为流InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);SqlSession sqlSession = factory.openSession();SqlSession sqlSession2 = factory.openSession();//这⾥不调⽤SqlSession的api,⽽是获得了接⼝对象,调⽤接⼝中的⽅法。使用JDK动态代理产生代理对象IUserDao userDao = sqlSession.getMapper(IUserDao.class);IUserDao userDao2 = sqlSession2.getMapper(IUserDao.class);// 第一次执行查询List<User> userList = userDao.findAll();userList.forEach(user -> {System.out.println(user.toString());});System.out.println("*************************************************");// 第二次执行相同的查询List<User> userList2 = userDao2.findAll();userList2.forEach(user -> {System.out.println(user.toString());});
}

我们开启创建了两个不同的sqlSession分别是sqlSession和sqlSession2,然后执行相同的查询。
输出如下:

Opening JDBC Connection
Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
Created connection 1011044643.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@3c435123]
==>  Preparing: SELECT * FROM `user`
==> Parameters: 
<==    Columns: id, name
<==        Row: 1, 张飞
<==        Row: 2, 关羽
<==        Row: 3, 孙权
<==      Total: 3
User{id=1, username='张飞'}
User{id=2, username='关羽'}
User{id=3, username='孙权'}
*************************************************
Opening JDBC Connection
Created connection 156199931.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@94f6bfb]
==>  Preparing: SELECT * FROM `user`
==> Parameters: 
<==    Columns: id, name
<==        Row: 1, 张飞
<==        Row: 2, 关羽
<==        Row: 3, 孙权
<==      Total: 3
User{id=1, username='张飞'}
User{id=2, username='关羽'}
User{id=3, username='孙权'}

此时我们就看到,第二次查询并没有利用到缓存,而是又开始了一次新的查询。所以这种跨sqlsession是不能使用缓存的。不是不存在,而是使用不到。

2.2、注意点2

我们既然知道了在sqlseeion不同的时候,缓存是不能利用的,那么问题来了,你的以后的日常开发中这个sqlsession他会经常变吗,如果变了,那岂不是走不了缓存了。
实际上就是经常变的,比如你页面上有一个按钮就是搜索,每次都会查询数据库,实际上你每次点击都会建立新的sqlsession,因为这玩意是伴随着连接的。你每次请求都会创建新的连接,那你的缓存就没用,除非你在你的service里面连着调用了两次一样的查询,这个,,,你没事吧。
所以你的一级缓存大部分情况下,没卵用。
那你为啥说每次请求都是单独的sqlSession或者单独的jdbc connection呢,因为这里牵扯到要控制事务,不同请求必须是独立隔离的连接,这样才能独立控制事务,如果共享事务,可能会造成混乱。比如请求1,请求2开启一个连接,那请求1还没完呢,你请求2就提交了事务,这不就寄了。

2.3、注意点3

那么查询是不是不需要事务呢,查询是不是就可以共享连接了,不对,查询也需要,select后面可以加锁。需要事务。
后面的二级缓存,这里也是需要的。后面我们再说,而且设计的时候肯定是统一处理的,不会说为了你一个加锁或者二级缓存就去区分开代码处理。

所以基于一级缓存的苛刻条件,我们知道这个一级缓存其实在使用者层面,他用处不大,实际上他也是在内部他自己的使用的。
既然我们说了他没啥用,那他设计出来干嘛呢,必然是有用处,下面我们来看下源码来分析一下他的用处。但是在进入源码之前,我们先来看一个设计模式,就是适配器设计模式。

3、适配器设计模式

我们在前面看到了mybatis的设计结构,大致如下。
在这里插入图片描述
我们看到首先他是一个Executor的接口,然后下面一个BaseExecutor来实现了这个接口,下面又是三个子类继承了BaseExecutor这个类,这其实就是一个适配器模式。我们看下他的类结构。
在这里插入图片描述

1、什么是适配器。

这个玩意一般资料都会拿出一个电压的例子,比如中国的常规电压是220伏特,人能承受的大概是36伏特,那我们要是碰到220伏特就寄了,于是我们可以使用一个变压器,放在中间。
在这里插入图片描述
这个变压器的作用就是适配器,这个例子你是不是在无数地方都看吐了,我们还是直接在代码说吧。
比如此时有一个Servlet的接口,里面有五个接口(我简化了一下)。

public interface Servlet {void init();void service();void destory();String getServletInfo();void getServletConfig();
}

此时我想实现一个自己的Servlet就叫做MyServlet,其中我只想实现 String getServletInfo();这个方法,但是java的语法就是我只要实现这个接口,就得实现他所有的方法,这个压力太大,等于直接把220V的电压给我了,我不想这么整,所以此时需要一个变压器。也就是适配器登场。
此时我们在创建一个类,去实现这个接口,如下:

public abstract class MyServletAdapter implements Servlet{@Overridepublic void init() {}@Overridepublic void service() {}@Overridepublic void destory() {}@Overridepublic String getServletInfo() {return null;}public abstract void getServletConfig();
}

你看到我们此时实现了这个接口,并且实现了其中四个方法,最后一个我们做成了抽象方法,其他四个你想不想实现看你。最后一个我们做成抽象的,给我们自己的那个servlet去实现。
于是就变为这样、

public  class MyServlet extends MyServletAdapter{@Overridepublic void getServletConfig() {System.out.println("我自己的servlet实现单独的一个方法");}
}

此时我们的这个类就完成了变压器到达终端,此时就只需要实现一个即可,而因为这个在变压器里面是抽象方法,所以这里是存在必须实现的约束的。还是有接口的作用。这就是适配器模式。

2、mybatis中的适配器

这样我们知道了适配器模式,那么其实上面那张图中的BaseExecutor就是适配器了。
在这里插入图片描述
BaseExecutor把接口中的一部分方法实现了,没实现的最后交个那三个子类各自实现各自定制化的东西,而公共的则由适配器里面去实现了。子类里面的还能用这些公共的,节省了代码量。这和模板设计模式很像。但虽然像,只是他们实现过程中表现出来的抽象,因为设计模式都是为了复用,抽象,扩展这些特点的,而适配器模式实际上出发点他是为了"变压"。

4、一级缓存的静态源码分析

我们说其实不管你哪个Excutor子类都是有这个缓存的,所以我么能猜出来,这个缓存功能其实是放在适配器里面的公共部分让子类使用的,我们看下这个适配器BaseExecutor。

public abstract class BaseExecutor implements Executor {......protected PerpetualCache localCache;// 一级缓存protected PerpetualCache localOutputParameterCache;// 存储过程的,一般不用@Overridepublic <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {// 根据你传进来的参数,动态生成sql,返回的boundSql就有这个sqlBoundSql boundSql = ms.getBoundSql(parameter);// 为本次查询创建缓存的keyCacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);//重载方法,实现查询return query(ms, parameter, rowBounds, resultHandler, key, boundSql);}// 在这里重载@SuppressWarnings("unchecked")@Overridepublic <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());// 判断执行器是不是被关闭,关闭直接抛出异常if (closed) {throw new ExecutorException("Executor was closed.");}// 清空本地缓存,如果queryStack为0,就要清空本地缓存if (queryStack == 0 && ms.isFlushCacheRequired()) {clearLocalCache();}List<E> list;try {queryStack++;// localCache.getObject(key)取出一级缓存里面的查询结果list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;if (list != null) {// 如果缓存中有,就直接返回缓存的handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);} else {//如果缓存中没有本次查找的值,那么queryFromDatabase方法从数据库中查询list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);}} finally {queryStack--;}if (queryStack == 0) {// 执行延迟加载for (DeferredLoad deferredLoad : deferredLoads) {deferredLoad.load();}// issue #601deferredLoads.clear();if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {// issue #482clearLocalCache();}}return list;}}private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {List<E> list;// 在缓存中添加占位对象,此处的占位符和延迟加载有关,见DeferredLoad#canLoad()localCache.putObject(key, EXECUTION_PLACEHOLDER);try {//doQuery是真正的最后的去执行读操作list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);} finally {// 从缓存中溢出占位对象localCache.removeObject(key);}// 查询完把结果添加到缓存中localCache.putObject(key, list);// 暂时忽略,存储过程相关if (ms.getStatementType() == StatementType.CALLABLE) {localOutputParameterCache.putObject(key, parameter);}return list;}

所以这里大致你能看到这个过程,就是他会读取缓存,没有缓存就去读取数据库,并且设置缓存,这是我们基于代码看到的效果,实际上的过程我们需要debug来看一下,就能看到如何读取,并且跨sqlSession是如何不生效的,这个可以直接在debug看到,每个sqlsession下面有不同的localCache,实现了缓存隔离。这个我们下面再来debug追踪源码。这里先简单贴个图。
在这里插入图片描述

相关文章:

橘子学Mybatis08之Mybatis关于一级缓存的使用和适配器设计模式

前面我们说了mybatis的缓存设计体系&#xff0c;这里我们来正式看一下这玩意到底是咋个用法。 首先我们是知道的&#xff0c;Mybatis中存在两级缓存。分别是一级缓存(会话级)&#xff0c;和二级缓存(全局级)。 下面我们就来看看这两级缓存。 一、准备工作 1、准备数据库 在此之…...

看图说话:Git图谱解读

很多新加入公司的同学在使用Git各类客户端管理代码的过程中对于Git图谱解读不太理解&#xff0c;我们常用的Git客户端是SourceTree&#xff0c;配合P4Merge进行冲突解决基本可以满足日常工作大部分需要。不同的Git客户端工具对图谱展示会有些许差异&#xff0c;以下是SourceTre…...

linux新增用户,指定home目录和bash脚本且加入到sudoer列表

前言 近3年一直用自动化脚本&#xff0c;搞得连useradd命令都不会用了。哈哈。 今天还碰到一个问题&#xff0c;有个系统没有‘useradd’和‘passwd’命令&#xff0c;直接蒙了。当然直接用apt install就能安装&#xff0c;不然还得自己编译折腾一会。新建用户 useradd -d /h…...

经典目标检测YOLO系列(三)YOLOV3的复现(1)总体网络架构及前向处理过程

经典目标检测YOLO系列(三)YOLOV3的复现(1)总体网络架构及前向处理过程 和之前实现的YOLOv2一样&#xff0c;根据《YOLO目标检测》(ISBN:9787115627094)一书&#xff0c;在不脱离YOLOv3的大部分核心理念的前提下&#xff0c;重构一款较新的YOLOv3检测器&#xff0c;来对YOLOv3有…...

OpenGL/C++_学习笔记(四)空间概念与摄像头

汇总页 上一篇: OpenGL/C_学习笔记&#xff08;三&#xff09; 绘制第一个图形 OpenGL/C_学习笔记&#xff08;四&#xff09;空间概念与摄像头 空间概念与摄像头前置科技树: 线性代数空间概念流程简述各空间相关概念详述 空间概念与摄像头 前置科技树: 线性代数 矩阵/向量定…...

C语言2024-1-27练习记录

#define _CRT_SECURE_NO_WARNINGS 1#include<stdio.h>//int main() //{ // char c[15] { I, ,a,n,d, ,you,. }; // int i; // for(i 0; i < 15; i) //这个地方有几个地方需要注意一下&#xff0c;首先变量指定之后必须要加上英文状态下的分号 // printf("%c&q…...

深入解析HTTPS:安全机制全方位剖析

随着互联网的深入发展&#xff0c;网络传输中的数据安全性受到了前所未有的关注。HTTPS&#xff0c;作为HTTP的安全版本&#xff0c;为数据在客户端和服务器之间的传输提供了加密和身份验证&#xff0c;从而确保了数据的机密性、完整性和身份真实性。本文将详细探讨HTTPS背后的…...

【197】JAVA8调用阿里云对象存储API,保存图片并获取图片URL地址。

实际工作中&#xff0c;需要用阿里云对象存储保存图片&#xff0c;并且在上传图片到阿里云对象存储服务器后&#xff0c;获取图片在阿里云对象存储服务器的URL地址&#xff0c;以便给 WEB 前端显示。 阿里云对象存储上传图片的工具类 package zhangchao;import com.aliyun.os…...

2024.1.24 C++QT 作业

思维导图 练习题 1.提示并输入一个字符串&#xff0c;统计该字符中大写、小写字母个数、数字个数、空格个数以及其他字符个数 #include <iostream> #include <string.h> #include <array> using namespace std;int main() {string str;cout << "…...

jenkins部署过程记录

一、jenkins部署git链接找不到 原因分析&#xff1a; 机器的git环境不是个人git的权限&#xff0c;所以clone不了。Jenkins的master节点部署机器已经部署较多其他的job在跑&#xff0c;如果直接修改机器的git配置&#xff0c;很可能影响到其他的job clone 不了代码&#xff0c…...

JS-策略设计模式

设计模式&#xff1a;针对特定问题提出的简洁优化的解决方案 一个问题有多种处理方案&#xff0c;而且处理方案随时可能增加或减少比如&#xff1a;商场满减活动 满50元减5元满100元减15元满200元减35元满500元减100元 // 满减金额计算函数 function count(money, type) {if …...

漏洞复现-EduSoho任意文件读取漏洞(附漏洞检测脚本)

免责声明 文章中涉及的漏洞均已修复&#xff0c;敏感信息均已做打码处理&#xff0c;文章仅做经验分享用途&#xff0c;切勿当真&#xff0c;未授权的攻击属于非法行为&#xff01;文章中敏感信息均已做多层打马处理。传播、利用本文章所提供的信息而造成的任何直接或者间接的…...

「QT」QString类的详细说明

✨博客主页何曾参静谧的博客📌文章专栏「QT」QT5程序设计📚全部专栏「VS」Visual Studio「C/C++」C/C++程序设计「UG/NX」BlockUI集合「Win」Windows程序设计「...

微信小程序-04

rpx&#xff08;responsive pixel&#xff09;是微信小程序独有的&#xff0c;用来解决屏适配的尺寸单位。 import 后跟需要导入的外联样式表的相对路径&#xff0c;用 ; 表示语句结束。 定义在 app.wxss 中的样式为全局样式&#xff0c;作用于每一个页面。 在页面的 .wxss 文…...

什么是数据库的三级模式两级映象?

三级模式两级映象结构图 概念 三级模式 内模式&#xff1a;也称为存储模式&#xff0c;是数据物理结构和存储方式的描述&#xff0c;是数据在数据库内部的表示方式。定义所有的内部记录类型、索引和文件组织方式&#xff0c;以及数据控制方面的细节。模式&#xff1a;又称概念…...

初识人工智能,一文读懂机器学习之逻辑回归知识文集(6)

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…...

2024 CKA 题库 | 15、备份还原 etcd

不等更新题库 文章目录 15、备份还原 etcd题目:考点&#xff1a;参考链接:解答:备份快照恢复快照 检查 15、备份还原 etcd 题目: 设置配置环境 此项目无需更改配置环境。但是&#xff0c;在执行此项目之前&#xff0c;请确保您已返回初始节点。 [candidatemaster01] $ exit #…...

基于Matlab/Simulink直驱式风电储能制氢仿真模型

接着还是以直驱式风电为DG中的研究对象&#xff0c;上篇博客考虑的风电并网惯性的问题&#xff0c;这边博客主要讨论功率消纳的问题。 考虑到风速是随机变化的&#xff0c;导致风电输出功率的波动性和间歇性问题突出&#xff1b;随着其应用规模的不断扩大以及风电在电网中渗透率…...

计算机网络(第六版)复习提纲16

三 IP地址与MAC地址 1 IP层只能看到IP数据报 2 路由器只根据目的IP地址进行转发 3 局域网的链路层只能看到MAC帧 4 IP层抽象的互联网屏蔽了下层的复杂细节&#xff0c;在网络层讨论问题能够使用统一的、抽象的IP地址来研究主机和主机或路由间的通信 问题&#xff1a; 1 主机或路…...

【AndroidStudio】2022.3Giraffe连接超时,更换下载源,使用本地gradle,版本对应问题

记录了使用AndroidStudio2022.3 Giraffe版本在搭建环境时遇到的问题&#xff0c;包括连接超时&#xff0c;gradle无法读取等。 如果只看如何正确的配置&#xff0c;直接跳转第3节 配置汇总 1 连接超时 项目一开始会自动下载gardle文件来加载项目 1.1 Connect timed out 基…...

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站&#xff0c;会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后&#xff0c;网站没有变化的情况。 不熟悉siteground主机的新手&#xff0c;遇到这个问题&#xff0c;就很抓狂&#xff0c;明明是哪都没操作错误&#x…...

Docker 离线安装指南

参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性&#xff0c;不同版本的Docker对内核版本有不同要求。例如&#xff0c;Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本&#xff0c;Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...

C++实现分布式网络通信框架RPC(3)--rpc调用端

目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中&#xff0c;我们已经大致实现了rpc服务端的各项功能代…...

【入坑系列】TiDB 强制索引在不同库下不生效问题

文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...

Cesium1.95中高性能加载1500个点

一、基本方式&#xff1a; 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...

Neo4j 集群管理:原理、技术与最佳实践深度解析

Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...

【单片机期末】单片机系统设计

主要内容&#xff1a;系统状态机&#xff0c;系统时基&#xff0c;系统需求分析&#xff0c;系统构建&#xff0c;系统状态流图 一、题目要求 二、绘制系统状态流图 题目&#xff1a;根据上述描述绘制系统状态流图&#xff0c;注明状态转移条件及方向。 三、利用定时器产生时…...

RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)

RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发&#xff0c;后来由Pivotal Software Inc.&#xff08;现为VMware子公司&#xff09;接管。RabbitMQ 是一个开源的消息代理和队列服务器&#xff0c;用 Erlang 语言编写。广泛应用于各种分布…...

RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill

视觉语言模型&#xff08;Vision-Language Models, VLMs&#xff09;&#xff0c;为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展&#xff0c;机器人仍难以胜任复杂的长时程任务&#xff08;如家具装配&#xff09;&#xff0c;主要受限于人…...

Web中间件--tomcat学习

Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机&#xff0c;它可以执行Java字节码。Java虚拟机是Java平台的一部分&#xff0c;Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...