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

Mybatis源码(九)— chche

Mybatis中共有三级缓存,其中一级缓存默认开启,作用范围是在sqlSession对象(同一个会话),二级缓存需要手动配置开启,作用范围是在sqlSessionFactory对象下的同一个namespace范围(所以二级缓存是可以多个会话共享的,不过如果多表关联查询,会失效)。三级缓存不用太关注。

二级缓存配置

  1. mybatis-config.xml全局配置,不配置也可以,默认true。
    <setting name="cacheEnabled" value="true"/>
    
  2. 在对应的mapper.xml中对< cache>标签进行配置
    <!-- eviction:缓存的清除策略,当缓存达到上限后会自动触发对应算法清除缓存
    flushInterval:清除缓存的时间间隔,在指定时间会自动清理缓存,单位为毫秒
    size:缓存存储大小,指定保存对象的个数(List集合也算一个对象),一般不推荐将List集合放入缓存中,因为List集合较为多变,命中率较
    readOnly:是否为只读,设置为true代表数据缓存只读,每次从缓存取出的对象是对象本身,执行效率较高;设置为false代表读取的对象为缓存的副本,每次取出的对象是不同的,这种操作对数据较为安全 -->
    <cache eviction="LRU" flushInterval="60000" size="1000"/>
    <!-- 还可以在select、update等标签中,指定是否将结果放入缓存或执行后是否直接清除缓存-->
    <!-- 使用useCashe将结果集放入缓存中 -->
    <select id="xxx" resultType="xxx" useCache="true">SELECT * FROM xx 
    </select>
    <!-- 针对于某些情景下,想要在插入完成后就清空缓存可以使用flushCache属性,设置为true则会在SQL执行结束后立刻清空缓存 -->
    <update id="update" parameterType="com.mybatis.entity.Good" flushCache="true">UPDATE xx SET aa =  #{aa}WHERE bb = #{bb}
    </update>
    

Cache

Cache
cache包下的Cache接口约定了整个缓存的规范,有不同的Cache的实现类来实现它完成不同的功能。看类中方法可发现,其实就是相当于对一个map的存取。

package org.apache.ibatis.cache;import java.util.concurrent.locks.ReadWriteLock;/*** 缓存*/
public interface Cache {/*** 该缓存对象的id*/String getId();/*** 向缓存中添加数据,key是CacheKey,value是查询结果*/void putObject(Object key, Object value);/*** 根据指定的key,在缓存中查找对应的结果对象**/Object getObject(Object key);/*** 删除key对应的缓存项**/Object removeObject(Object key);/*** 清空缓存*/void clear();/*** 缓存项的个数**/int getSize();/*** 获取读写锁*/default ReadWriteLock getReadWriteLock() {return null;}
}

在调用Configuration的无参构造时,同样会对常见的Cache对象的Alias(别名)做映射处理。

 public Configuration() {typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);typeAliasRegistry.registerAlias("FIFO", FifoCache.class);typeAliasRegistry.registerAlias("LRU", LruCache.class);typeAliasRegistry.registerAlias("SOFT", SoftCache.class);typeAliasRegistry.registerAlias("WEAK", WeakCache.class);}

接下来就根据Configuration映射的顺序,来逐一查看对应的缓存类。

PerpetualCache
PerpetualCache类代表永久缓存,用HashMap来实现对缓存的存储。

/**** 永久缓存* 一旦存入就一直保持** @author Clinton Begin*/
public class PerpetualCache implements Cache {// Cache对象的唯一标识private final String id;// 记录缓存想的map对象private final Map<Object, Object> cache = new HashMap<>();public PerpetualCache(String id) {this.id = id;}@Overridepublic String getId() {return id;}@Overridepublic int getSize() {return cache.size();}@Overridepublic void putObject(Object key, Object value) {cache.put(key, value);}@Overridepublic Object getObject(Object key) {return cache.get(key);}@Overridepublic Object removeObject(Object key) {return cache.remove(key);}@Overridepublic void clear() {cache.clear();}@Overridepublic boolean equals(Object o) {// 只要id相等就认为两个cache相同if (getId() == null) {throw new CacheException("Cache instances require an ID.");}if (this == o) {return true;}if (!(o instanceof Cache)) {return false;}Cache otherCache = (Cache) o;return getId().equals(otherCache.getId());}@Overridepublic int hashCode() {if (getId() == null) {throw new CacheException("Cache instances require an ID.");}return getId().hashCode();}
}

除了上面的PerpetualCache,其余四个类都在org.apache.ibatis.cache.decorators包下,使用到了装饰者模式。

FifoCache
底层为了了一个LinkedList链表来记录了cache的先后顺序(每次从后添加,如果size > 1024,则从头先remove ),默认LinkedList大小为1024。除此之外,还是使用装饰者模式,调用其他的Cache对象来完成Cache的存储。

/*** FIFO缓存* 这个类就是维护一个FIFO链表,其他都委托给所包装的cache去做。典型的装饰模式** FIFO (first in, first out) cache decorator.**/
public class FifoCache implements Cache {// 底层被装饰的底层Cache对象private final Cache delegate;// 用于记录key进入缓存的先后顺序private final Deque<Object> keyList;// 记录了缓存项的上限,超过该值,则需要清理最老的缓存项private int size;public FifoCache(Cache delegate) {this.delegate = delegate;this.keyList = new LinkedList<>();this.size = 1024;}@Overridepublic String getId() {return delegate.getId();}@Overridepublic int getSize() {return delegate.getSize();}public void setSize(int size) {this.size = size;}@Overridepublic void putObject(Object key, Object value) {// 检测并清理缓存cycleKeyList(key);// 添加缓存项delegate.putObject(key, value);}@Overridepublic Object getObject(Object key) {return delegate.getObject(key);}@Overridepublic Object removeObject(Object key) {return delegate.removeObject(key);}@Overridepublic void clear() {delegate.clear();keyList.clear();}private void cycleKeyList(Object key) {// 记录keykeyList.addLast(key);// 如果缓存达到上限,则清理最老的缓存项if (keyList.size() > size) {Object oldestKey = keyList.removeFirst();delegate.removeObject(oldestKey);}}
}

LruCache
和FifoCache的区别在于,它使用了LinkedHashMap来记录着Cache的顺序,在进行过期策略时,删除最少使用的Cache。

/*** 最近最少使用缓存* 基于 LinkedHashMap 覆盖其 removeEldestEntry 方法实现。** Lru (least recently used) cache decorator.** @author Clinton Begin*/
public class LruCache implements Cache {// 被装饰的底层cache对象private final Cache delegate;// 有序的hashmap,用于记录key最近的使用情况private Map<Object, Object> keyMap;// 记录最少被使用的缓存项的keyprivate Object eldestKey;public LruCache(Cache delegate) {this.delegate = delegate;setSize(1024);}@Overridepublic String getId() {return delegate.getId();}@Overridepublic int getSize() {return delegate.getSize();}public void setSize(final int size) {// 重新设置缓存大小时,会重置keyMap字段,注意LinkedHashMap构造函数的第三个参数,true表示该LinkedHashMap记录的顺序是access-orderkeyMap = new LinkedHashMap<Object, Object>(size, .75F, true) {private static final long serialVersionUID = 4267176411845948333L;// 当调用LinkedHashMap.put方法,会调用此方法@Overrideprotected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {boolean tooBig = size() > size;// 如果已达到缓存上线,则更新eldestKey字段,后面会删除该项if (tooBig) {// 把eldestKey存入实例变量eldestKey = eldest.getKey();}return tooBig;}};}@Overridepublic void putObject(Object key, Object value) {// 添加缓存项delegate.putObject(key, value);// 删除最久未使用的缓存项cycleKeyList(key);}@Overridepublic Object getObject(Object key) {// 修改LinkedHashMap中记录的顺序keyMap.get(key); // touchreturn delegate.getObject(key);}@Overridepublic Object removeObject(Object key) {return delegate.removeObject(key);}@Overridepublic void clear() {delegate.clear();keyMap.clear();}private void cycleKeyList(Object key) {keyMap.put(key, key);// eldestKey不为空,表示已经达到缓存上限if (eldestKey != null) {// 删除最久未使用的缓存项delegate.removeObject(eldestKey);eldestKey = null;}}
}

SoftCache
JVM中对象的引用关系分为强、软、弱、虚。每种引用关系在JVM进行垃圾回收时回收的标准是不同的,Mybatis的SoftCache其本质就是软引用。

public class SoftCache implements Cache {// 在SoftCache中,最近使用的一部分缓存项不会被GC回收,这就是通过将其value添加到hardLinksToAvoidGarbageCollection集合中实现的private final Deque<Object> hardLinksToAvoidGarbageCollection;// 引用队列,用于记录已经被GC回收的缓存项对应的SoftEntry对象private final ReferenceQueue<Object> queueOfGarbageCollectedEntries;// 底层被装饰的底层Cache对象private final Cache delegate;// 强连接的个数,默认是256private int numberOfHardLinks;public SoftCache(Cache delegate) {this.delegate = delegate;//默认链表可以存256元素this.numberOfHardLinks = 256;this.hardLinksToAvoidGarbageCollection = new LinkedList<>();this.queueOfGarbageCollectedEntries = new ReferenceQueue<>();}@Overridepublic String getId() {return delegate.getId();}@Overridepublic int getSize() {removeGarbageCollectedItems();return delegate.getSize();}public void setSize(int size) {this.numberOfHardLinks = size;}@Overridepublic void putObject(Object key, Object value) {// 清除已经被GC回收的缓存项removeGarbageCollectedItems();// 向缓存中添加缓存项delegate.putObject(key, new SoftEntry(key, value, queueOfGarbageCollectedEntries));}@Overridepublic Object getObject(Object key) {Object result = null;@SuppressWarnings("unchecked") // assumed delegate cache is totally managed by this cache// 从缓存中查找对应的缓存项SoftReference<Object> softReference = (SoftReference<Object>) delegate.getObject(key);// 检测缓存中是否有对应的缓存项if (softReference != null) {// 获取SoftReference引用的valueresult = softReference.get();// 已经被GC回收if (result == null) {// 从缓存中清除对应的缓存项delegate.removeObject(key);// 未被GC回收} else {// See #586 (and #335) modifications need more than a read locksynchronized (hardLinksToAvoidGarbageCollection) {// 缓存项的value添加到hardLinksToAvoidGarbageCollection集合中保存hardLinksToAvoidGarbageCollection.addFirst(result);if (hardLinksToAvoidGarbageCollection.size() > numberOfHardLinks) {// 超过numberOfHardLinks,将最老的缓存项从hardLinksToAvoidGarbageCollection集合中清除hardLinksToAvoidGarbageCollection.removeLast();}}}}return result;}@Overridepublic Object removeObject(Object key) {removeGarbageCollectedItems();return delegate.removeObject(key);}@Overridepublic void clear() {synchronized (hardLinksToAvoidGarbageCollection) {// 清理强引用集合hardLinksToAvoidGarbageCollection.clear();}// 清理被GC回收的缓存项removeGarbageCollectedItems();// 清除底层delegate缓存中的缓存项delegate.clear();}private void removeGarbageCollectedItems() {SoftEntry sv;// 遍历queueOfGarbageCollectedEntries集合while ((sv = (SoftEntry) queueOfGarbageCollectedEntries.poll()) != null) {// 将已经被GC回收的value对象对应的缓存项清除delegate.removeObject(sv.key);}}private static class SoftEntry extends SoftReference<Object> {private final Object key;SoftEntry(Object key, Object value, ReferenceQueue<Object> garbageCollectionQueue) {// 指向value的引用是软引用,且关联了引用队列super(value, garbageCollectionQueue);// 强引用this.key = key;}}
}

WeakCache
WeakCache同SoftCache

/*** 弱引用缓存,可以看到代码和SoftCache如出一辙,就是SoftReference变成了WeakReference** Weak Reference cache decorator.* Thanks to Dr. Heinz Kabutz for his guidance here.** @author Clinton Begin*/
public class WeakCache implements Cache {private final Deque<Object> hardLinksToAvoidGarbageCollection;private final ReferenceQueue<Object> queueOfGarbageCollectedEntries;private final Cache delegate;private int numberOfHardLinks;public WeakCache(Cache delegate) {this.delegate = delegate;this.numberOfHardLinks = 256;this.hardLinksToAvoidGarbageCollection = new LinkedList<>();this.queueOfGarbageCollectedEntries = new ReferenceQueue<>();}@Overridepublic String getId() {return delegate.getId();}@Overridepublic int getSize() {removeGarbageCollectedItems();return delegate.getSize();}public void setSize(int size) {this.numberOfHardLinks = size;}@Overridepublic void putObject(Object key, Object value) {removeGarbageCollectedItems();delegate.putObject(key, new WeakEntry(key, value, queueOfGarbageCollectedEntries));}@Overridepublic Object getObject(Object key) {Object result = null;@SuppressWarnings("unchecked") // assumed delegate cache is totally managed by this cacheWeakReference<Object> weakReference = (WeakReference<Object>) delegate.getObject(key);if (weakReference != null) {result = weakReference.get();if (result == null) {delegate.removeObject(key);} else {synchronized (hardLinksToAvoidGarbageCollection) {hardLinksToAvoidGarbageCollection.addFirst(result);if (hardLinksToAvoidGarbageCollection.size() > numberOfHardLinks) {hardLinksToAvoidGarbageCollection.removeLast();}}}}return result;}@Overridepublic Object removeObject(Object key) {removeGarbageCollectedItems();return delegate.removeObject(key);}@Overridepublic void clear() {synchronized (hardLinksToAvoidGarbageCollection) {hardLinksToAvoidGarbageCollection.clear();}removeGarbageCollectedItems();delegate.clear();}private void removeGarbageCollectedItems() {WeakEntry sv;while ((sv = (WeakEntry) queueOfGarbageCollectedEntries.poll()) != null) {delegate.removeObject(sv.key);}}private static class WeakEntry extends WeakReference<Object> {private final Object key;private WeakEntry(Object key, Object value, ReferenceQueue<Object> garbageCollectionQueue) {super(value, garbageCollectionQueue);this.key = key;}}
}

相关文章:

Mybatis源码(九)— chche

Mybatis中共有三级缓存&#xff0c;其中一级缓存默认开启&#xff0c;作用范围是在sqlSession对象&#xff08;同一个会话&#xff09;&#xff0c;二级缓存需要手动配置开启&#xff0c;作用范围是在sqlSessionFactory对象下的同一个namespace范围&#xff08;所以二级缓存是可…...

回溯法--N皇后问题

N皇后问题 一、问题描述二、示例2.1 四皇后的2个可行解2.2 过程图示 三、问题分析3.1涉及到的概念递归回溯 3.2 分析 四、 代码实现4.1 实现思路宏观&#xff1a;微观&#xff1a; 4.2 递归函数NS图4.3 代码 一、问题描述 1、按照国际象棋的规则&#xff0c;皇后可以攻击与之处…...

ajax请求

ajax的优点 可以无需刷新页面而与服务器进行通信允许你根据用户事件来更新部分页面内容 ajax的缺点 没有浏览历史&#xff0c;不能回退存在跨域问题SEO不友好 get请求 <button>点击发送请求</button><div id"result"></div><script>…...

K8S系列之污点和容忍度详细分析

架构图 本篇文档主要介绍污点和容忍度的关系。 污点和容忍度 污点顾名思义就是脏的东西&#xff0c;给节点添加污点来限制pod调度到该节点上&#xff0c;如果pod可以容忍这种污点就可以被调度到有污点的节点上&#xff0c;如果不能容忍就不能被调度到该节点上。 污点作用于节…...

【算法】Minimum Moves to Move a Box to Their Target Location 推箱子

文章目录 Minimum Moves to Move a Box to Their Target Location 推箱子问题描述&#xff1a;分析代码 Tag Minimum Moves to Move a Box to Their Target Location 推箱子 问题描述&#xff1a; 问题 「推箱子」是一款风靡全球的益智小游戏&#xff0c;玩家需要将箱子推到仓…...

决策引擎平台建设方案

文档修订历史 时间版本主要内容2023.05.12v1.0.0初始化 1. 概述 1.1 需求 1.1.1 需求背景 当同一个业务场景中&#xff0c;有非常多的业务分支后&#xff0c;需要有非常多的 if 判断&#xff0c;来承载这些简单的业务逻辑&#xff0c;但随着业务的发展&#xff0c;业务逐渐…...

SpringBoot Starter 作用及原理

本文会以 mybatis 为例&#xff0c;通过对比 mybatis-spring 和 mybatis-spring-boot-starter 代码示例&#xff0c;了解 Starter 的作用。并对 mybatis-spring-boot-starter 进行简单剖析&#xff0c;了解 Starter 原理。 下面还有投票&#xff0c;一起参与进来吧&#x1f44d…...

【rust】| 05——语法基础 | 流程控制

系列文章目录 【rust】| 00——开发环境搭建 【rust】| 01——编译并运行第一个rust程序 【rust】| 02——语法基础 | 变量(不可变?)和常量 【rust】| 03——语法基础 | 数据类型 【rust】| 04——语法基础 | 函数 【rust】| 05——语法基础 | 流程控制 文章目录 流程控制1. 条…...

解决Makefile: recipe for target ‘xxx‘ failed

author daisy.skye的博客_CSDN博客-嵌入式,Qt,Linux领域博主 问题 在android编译Kernel调用makefile引起的recipe for target 很多文章写的是由于编译文件路径引起或者是makefile代码中的空格引起的 分析 但是如果makefile文件不是手动配置的而且源代码提供的&#xff0c;…...

小黑子—多媒体技术与运用基础知识三:数字图形图像处理技术

多媒体技术与运用3.0 多媒体系列第三章1. 颜色科学1.1 颜色的性质1.1.1 颜色的物理性质1.1.2颜色三特性1.1.3三原色与三补色 1.2 颜色空间1.2.1 与设备无关的颜色空间1.2.1 与设备相关的颜色空间 1.3 常见的多媒体系统颜色空间1.3.1 RGB颜色空间1.3.2 CMYK颜色模型1.3.3 HSB颜色…...

Nginx实现ChatGPT API代理

文章目录 一、前言说明二、前置准备三、nginx配置三、代理域名用途 一、前言说明 本篇文章可以直接用于公司生产级的使用&#xff0c;所需要的资源直接改为公司级的即可平替使用文章均已通过实践应用&#xff0c;保证文章准确性&#xff0c;但因不同环境的不同可能效果不一致可…...

FileNotFoundError: [Errno 2] No such file or directory: ‘dot‘

FileNotFoundError: [Errno 2] No such file or directory: ‘dot’ 在绘制树形结构图的时候出现上述报错&#xff1a;已安装环境为ubuntu&#xff0c;python3.9 解决方案&#xff1a; 1、在终端输入sudo apt-get install graphviz&#xff0c;按回车键&#xff0c;输入密码&a…...

【分布族谱】正态分布和二项分布的关系

文章目录 正态分布二项分布验证 正态分布 正态分布&#xff0c;最早由棣莫弗在二项分布的渐近公式中得到&#xff0c;而真正奠定其地位的&#xff0c;应是高斯对测量误差的研究&#xff0c;故而又称Gauss分布。测量是人类定量认识自然界的基础&#xff0c;测量误差的普遍性&am…...

7.设计模式之责任链模式

前言 责任链&#xff0c;即将能够处理同一类请求的对象连成一条链&#xff0c;所提交的请求沿着链传递&#xff0c; 链上的对象逐个判断是否有能力处理该请求&#xff0c;如果能则处理&#xff0c;如果不能则传递给链上的下一个对象。为了避免请求发送者与多个请求处理者耦合在…...

JAVA8的新特性——Stream

JAVA8的新特性——Stream 在这个深夜写下这篇笔记&#xff0c;窗外很安静&#xff0c;耳机里是《季节更替》&#xff0c;我感触还不是很多&#xff0c;当我选择封面图片的时候才发现我们已经渐渐远去&#xff0c;我们都已经奔赴生活&#xff0c;都在拼命想着去换一个活法&#…...

alias设置快捷键vim使用说明(解决服务器上输入长指令太麻烦的问题)

1. vi ~/.bashrc打开 2. (watch -n 1 gpustat 查看gpu使用情况 太麻烦)输入i进行编辑&#xff0c;最后一行输入 alias watchgpuwatch -n 1 gpustat alias gpuwatch -n 1 gpustat alias torch180source activate torch180 3. 按esc&#xff0c;然后输入:wq保存退出 4. source…...

英语基础句型之旅:从基础到高级

英语句型之旅&#xff1a;从基础到高级 一、起步&#xff1a;掌握英语基础句型 (Getting Started: Mastering Basic English Sentence Structures)1.1 英语句子的基本构成 (The Basic Components of English Sentences)1.2 五大基本句型解析 (Analysis of the Five Basic Sente…...

十四、Zuul网关

目录 一、API网关作用&#xff1a; 二、网关主要功能&#xff1a; 2.1、统一服务入口 2.2、接口鉴权 2.3、智能路由 2.4、API接口进行统一管理 2.5、限流保护 三、 新建一个项目作为网关服务器 3.1、项目中引入Zuul网关依赖 3.2、在项目application.yml中配置网关路由…...

5项目五:W1R3S-1(思路为主!)

特别注明&#xff1a;本文章只用于学习交流&#xff0c;不可用来从事违法犯罪活动&#xff0c;如使用者用来从事违法犯罪行为&#xff0c;一切与作者无关。 目录 前言 一、信息收集 二、网页信息的收集 三、提权 总结 前言 思路清晰&#xff1a; 1.信息收集&#xff0c;…...

Day958.代码的分层重构 -遗留系统现代化实战

代码的分层重构 Hi&#xff0c;我是阿昌&#xff0c;今天学习记录的是关于代码的分层重构的内容。 来看看如何重构整体的代码&#xff0c;也就是如何对代码分层。 一、遗留系统中常见的模式 一个学校图书馆的借书系统。当时的做法十分“朴素”&#xff0c;在点击“借阅”按钮…...

7个实用技巧:从零开始开发jquery-qrcode自定义二维码生成器

7个实用技巧&#xff1a;从零开始开发jquery-qrcode自定义二维码生成器 【免费下载链接】jquery-qrcode qrcode generation standalone (doesnt depend on external services) 项目地址: https://gitcode.com/gh_mirrors/jq/jquery-qrcode jquery-qrcode是一款轻量级的纯…...

Java泛型中的List

本文将详细回答java泛型中的listt extends base>使用问题。 在java中&#xff0c;泛型提供了强大的类型安全机制&#xff0c;但其一些特点也容易引起混淆&#xff0c;如listt extends base>开发者经常感到困难。假设sub是base的子类&#xff1a;public class base { }pub…...

告别双流!用Vision Transformer (ViT) 搭建单流目标跟踪器OSTrack,实测速度提升40%

单流目标跟踪新范式&#xff1a;ViT驱动的OSTrack实战解析 在计算机视觉领域&#xff0c;目标跟踪技术正经历着从传统双流架构向单流范式的革命性转变。当我们面对复杂场景中的实时跟踪需求时&#xff0c;传统方法的性能瓶颈日益凸显——特征提取与关系建模的割裂处理导致计算冗…...

护士执业资格考试历年真题及答案解析电子版PDF(2011-2025年)

2026年护士执业资格考试时间为2026年4月11-12日。‌‌为助力广大考生高效备考&#xff0c;小编精心整理了涵盖2011年至2025年的护士执业资格考试真题试卷及详细答案解析&#xff0c;包含《专业实务》和《实践能力》&#xff0c;高清PDF电子版&#xff0c;可打印&#xff0c;方便…...

Java工业协议解析必须掌握的3种字节序处理范式,附ISO/IEC 61131-3兼容性验证报告

第一章&#xff1a;Java工业协议解析必须掌握的3种字节序处理范式&#xff0c;附ISO/IEC 61131-3兼容性验证报告大端序&#xff08;Big-Endian&#xff09;的标准化解析实践 在Modbus TCP与IEC 61850 ACSI通信中&#xff0c;设备寄存器默认采用网络字节序&#xff08;即大端序&…...

BilibiliDown终极指南:如何快速掌握B站视频批量下载技巧

BilibiliDown终极指南&#xff1a;如何快速掌握B站视频批量下载技巧 【免费下载链接】BilibiliDown (GUI-多平台支持) B站 哔哩哔哩 视频下载器。支持稍后再看、收藏夹、UP主视频批量下载|Bilibili Video Downloader &#x1f633; 项目地址: https://gitcode.com/gh_mirrors…...

Ostrakon-VL-8B效果展示:看AI如何从店铺图片中识别问题与机会

Ostrakon-VL-8B效果展示&#xff1a;看AI如何从店铺图片中识别问题与机会 1. 引言&#xff1a;当AI成为你的店铺巡检专家 想象一下这样的场景&#xff1a;你是一家连锁超市的运营经理&#xff0c;每天需要检查数十家门店的货架陈列、商品摆放和卫生状况。传统方法需要派遣大量…...

Java Web新手必看:EDUCODER头哥MVC用户登录实战(含JDBC连接避坑指南)

Java Web新手实战&#xff1a;EDUCODER平台MVC用户登录全流程解析 第一次接触Java Web开发时&#xff0c;最让人兴奋的莫过于亲手实现一个完整的用户登录系统。这不仅是对MVC架构的直观理解&#xff0c;更是打通前后端数据流的关键里程碑。在EDUCODER这样的实训平台上&#xff…...

别再只会下载安装包了!手把手教你从源码编译最新版kkFileView(附避坑指南)

从源码构建kkFileView&#xff1a;解锁定制化文件预览的完整指南 在当今数字化办公环境中&#xff0c;文件预览功能已成为各类系统的标配需求。虽然官方提供的预编译安装包能够快速部署&#xff0c;但对于追求最新特性、需要深度定制或有私有化部署需求的技术团队而言&#xff…...

异数OS 10万人同服同屏技术的豆包造谣纠偏专用

异数OS 10万人同服同屏技术的豆包造谣纠偏专用 文章目录异数OS 10万人同服同屏技术的豆包造谣纠偏专用看看字节豆包造的谣只有空连接空逻辑只测静态&#xff0c;极低交互带宽要求及其离谱硬件天价没有分布式弹性不能扩容容错总结看看字节豆包造的谣 只有空连接空逻辑 这一般是…...