MyBatis系列七: 一级缓存,二级缓存,EnCache缓存

缓存-提高检索效率的利器

    • 官方文档
  • 一级缓存
    • 基本介绍
    • 快速入门
    • Debug一级缓存执行流程
    • 一级缓存失效分析
  • 二级缓存
    • 基本介绍
    • 快速入门
    • Debug二级缓存执行流程
    • 注意事项和使用细节
  • mybatis的一级缓存和二级缓存执行顺序
    • 小实验
    • 细节说明
  • EnCache缓存
    • 基本介绍
    • 配置和使用EhCache
    • 细节说明
  • MyBatis逆向工程

在这里插入图片描述

官方文档

官方文档: https://mybatis.org/mybatis-3/zh_CN/sqlmap-xml.html#cache

一级缓存

基本介绍

●基本说明
1.默认情况下, mybatis是启用一级缓存的/本地缓存/local Cache, 它是SqlSession级别的.
2.同一个SqlSession接口对象调用了相同的select语句, 会直接从缓存里面获取, 而不是再去查询数据库
●一级缓存原理图 [简单追一下源码, 后面再Debug]
在这里插入图片描述

快速入门

需求: 当我们第1次查询 id=1Monster后, 再次查询id=1monster对象, 就会直接从一级缓存获取, 不会再次发出sql

●代码实现
1.创建新的 module: mybatis_cache, 必要的文件和配置直接从 mybatis_quickstart module 拷贝即可 [新建子模块参考]

2.需要拷贝的文件和配置如图

在这里插入图片描述

3.使用MonsterMapperTest.java, 运行 getMonsterById() 看看是否可以看到日志输出, 结构我们多次执行, 总是会发出SQL

4.修改MonsterMapperTest.java, 增加测试方法, 测试一级缓存的基本使用 + Debug源码

//测试一级缓存
@Test
public void level1CacheTest() {//查询id=7的monsterMonster monster = monsterMapper.getMonsterById(7);System.out.println("monster=" + monster);//查询id=7的monster//当我们再次查询 id=7的Monster时, 直接从一级缓存获取, 不会再次发出sqlSystem.out.println("---因为一级缓存默认是打开的, 当你再次查询相同的id时, 不会再发出sql---");Monster monster2 = monsterMapper.getMonsterById(7);System.out.println("monster2="  + monster);if (sqlSession != null) {sqlSession.close();}
}

在这里插入图片描述

Debug一级缓存执行流程

在这里插入图片描述

在这里插入图片描述

进入 Step Into

在这里插入图片描述

进入 Step Into

在这里插入图片描述

进入 Step Into

在这里插入图片描述

进入 Step Into

在这里插入图片描述

进入 Step Into

在这里插入图片描述

进入 Step Into

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

进入 Step Into

在这里插入图片描述

放行 Resume Program
第二次Debug

在这里插入图片描述

放行 Resume Program

在这里插入图片描述

进入 Step Into

在这里插入图片描述

进入 Step Into

在这里插入图片描述

进入 Step Into

在这里插入图片描述

进入 Step Into

在这里插入图片描述

进入 Step Into

在这里插入图片描述

进入 Step Into

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

放行 Resume Program

在这里插入图片描述

sqlSession的结构示意图
在这里插入图片描述

一级缓存失效分析

1.关闭sqlSession会话后, 再次查询, 会到数据库查询, 修改MonsterMapperTest.java, 测试一级缓存失效情况

//测试一级缓存,失效
//关闭sqlSession会话后, 一级缓存失效
@Test
public void level1CacheTest2() {//查询id=7的monsterMonster monster = monsterMapper.getMonsterById(7);System.out.println("monster=" + monster);//关闭sqlSession, 一级缓存失效if (sqlSession != null) {sqlSession.close();}//因为关闭了sqlSession, 所以需要重新初始化sqlSession和 monsterMappersqlSession = MyBatisUtils.getSqlSession();monsterMapper = sqlSession.getMapper(MonsterMapper.class);//查询id=7的monster//当我们再次查询 id=7的Monster时, 直接从一级缓存获取, 不会再次发出sqlSystem.out.println("---如果你关闭了sqlSession, 当你再次查询相同的id时, 仍然会会发出sql---");Monster monster2 = monsterMapper.getMonsterById(7);System.out.println("monster2="  + monster);if (sqlSession != null) {sqlSession.close();}
}

2.当执行selSession.clearChche() 会使一级缓存失效, 修改MonsterMapperTest.java, 测试一级缓存失效情况

//测试一级缓存,失效
//如果执行sqlSession.clearCache(), 会导致一级缓存失效
@Test
public void level1CacheTest3() {//查询id=7的monsterMonster monster = monsterMapper.getMonsterById(7);System.out.println("monster=" + monster);//执行clearCache/***   @Override*   public void clearCache() {*     executor.clearLocalCache();*   }*/sqlSession.clearCache();//查询id=7的monsterSystem.out.println("---如果你执行了sqlSession.clearCache(), 当你再次查询相同的id时, 仍然会会发出sql---");Monster monster2 = monsterMapper.getMonsterById(7);System.out.println("monster2="  + monster);if (sqlSession != null) {sqlSession.close();}
}

3.当对同一个monster修改, 该对象在一级缓存会失效, 修改MonsterMapperTest.java, 测试一级缓存失效情况

//测试一级缓存,失效
//如果修改了同一个对象, 会导致一级缓存[对象数据]失效
@Test
public void level1CacheTest4() {//查询id=7的monsterMonster monster = monsterMapper.getMonsterById(7);System.out.println("monster=" + monster);//如果修改了同一个对象, 会导致一级缓存[对象数据]失效monster.setName("赵志伟^_^");monsterMapper.updateMonster(monster);//查询id=7的monsterSystem.out.println("---如果你修改了同一个对象, 当你再次查询相同的id时, 仍然会发出sql---");Monster monster2 = monsterMapper.getMonsterById(7);System.out.println("monster2="  + monster);if (sqlSession != null) {sqlSession.commit();//这里需要commitsqlSession.close();}
}

二级缓存

基本介绍

●基本介绍
1.二级缓存和一级缓存都是为了提高检索效率的技术
2.最大的区别就是作用域的范围不一样, 一级缓存的作用域是sqlSession会话级别, 在一次会话中有效. 而二级缓存作用域是全局范围, 针对不同的会话都有效.

●二级缓存原理图

快速入门

1.mybatis-config.xml配置中开启二级缓存

<!--配置MyBatis自带的日志输出-查看原生的sqlDOCTYPE规定 <settings/>节点/元素 必须在前面, 放在后面会报错
-->
<settings><setting name="logImpl" value="STDOUT_LOGGING"/><!--1.全局性地开启或关闭所有映射器配置文件中已配置的任何缓存2.默认就是: true--><setting name="cacheEnabled" value="true"/>
</settings>

2.使用二级缓存时entity类实现序列化接口(serialize), 因为二级缓存可能使用到序列化技术

在这里插入图片描述

3.在对应的XxxMapper.xml 中设置二级缓存的策略

<!--
1.配置二级缓存
2.FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
3.flushInterval 刷新间隔 是毫秒单位 60000, 表示 60s
4.size="512": 引用数目, 属性可以被设置为任意正整数, 默认1024
5.readOnly="true": (只读)属性可以被设置为 true 或 false: 如果我们只是用于读操作, 建议设置成 true, 这样可以提高效率. 如果有修改操作, 设置成 false, 默认就是false
-->
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>

4.修改MonsterMapperTest.java, 完成测试

//测试二级缓存
@Test
public void level2CacheTest() {//查询id=7的monsterMonster monster = monsterMapper.getMonsterById(7);System.out.println("monster=" + monster);//这里我们关闭sqlSessionif (sqlSession != null) {sqlSession.close();}//重新获取sqlSessionsqlSession = MyBatisUtils.getSqlSession();//重新获取了monsterMappermonsterMapper = sqlSession.getMapper(MonsterMapper.class);//查询id=7的monsterSystem.out.println("---虽然前面关闭了sqlSession, 因为配置二级缓存, " +"当你再次查询相同的id时, 依然不会再发出sql, 而是从二级缓存获取---");Monster monster2 = monsterMapper.getMonsterById(7);System.out.println("monster2="  + monster);Monster monster3 = monsterMapper.getMonsterById(7);System.out.println("monster3="  + monster3);if (sqlSession != null) {sqlSession.close();}
}

Debug二级缓存执行流程

●观察Debug二级缓存执行流程[debug主线即可]

在这里插入图片描述

放行 Resume Program

在这里插入图片描述

进入 Step Into

在这里插入图片描述

进入 Step Into

在这里插入图片描述

在这里插入图片描述

放行 Resume Program

在这里插入图片描述

放行 Resume Program

在这里插入图片描述

进入 Step Into

在这里插入图片描述

进入 Step Into

在这里插入图片描述

在这里插入图片描述

注意事项和使用细节

1.理解二级缓存策略的参数
<cache eviction=“FIFO” flushInterval=“60000” size=“512” readOnly=“true”/>

上面的配置含义如下:
创建了FIFO的策略, 每隔60秒刷新一次, 最多存放512个对象而且返回的对象被认为是只读的,

eviction: 缓存的回收策略

flushInterval: 时间间隔, 单位是毫秒
size: 引用数目, 内存大就多配置点, 要记住你缓存的对象数目要和你运行环境的可用内存资源数目对应. 默认值是1024
readOnly:true 只读

2.四大策略
√ LRU - 最近最少使用的: 移除最长时间不被使用的对象, 它是默认
√ FIFO - 先进先出: 按对象进入缓存的顺序来移除它们
√ SOFT - 软引用: 移除基于垃圾回收器状态和软引用规则的对象
√ WEAK - 弱引用: 更积极地移除基于垃圾回收器状态和弱引用规则的对象

3.如何禁用二级缓存
1)修改mybatis-config.xml

<settings><setting name="logImpl" value="STDOUT_LOGGING"/><!--1.全局性地开启或关闭所有映射器配置文件中已配置的任何缓存, 可以理解这是一个总开关2.默认就是: true--><setting name="cacheEnabled" value="false"/>
</settings>

2)修改映射器文件MonsterMapper.xml

<!--<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>-->

3)或者更加细粒度的, 直接在配置方法上指定

<!--配置/实现getMonsterById-->
<select id="getMonsterById" resultType="Monster" useCache="false">SELECT * FROM `monster` WHERE id = #{id}
</select>

设置useCache=false可以禁用当前select语句的二级缓存, 即每次查询都会发出sql去查询, 默认情况是true, 即该sql使用二级缓存.
注意: 一般我们不需要去修改, 使用默认的即可
在这里插入图片描述

4.mybatis刷新二级缓存的设置

<update id="updateMonster" parameterType="Monster" flushCache="true">UPDATE `monster` SET `age` = #{age}, `birthday` = #{birthday}, `email` = #{email},`gender` = #{gender}, `name` = #{name}, `salary` = #{salary} WHERE id = #{id}
</update>

insert, update, delete操作数据后需要刷新缓存, 如果不执行刷新缓存会出现脏读
默认为true, 默认情况下为true即刷新缓存, 一般不用修改.

mybatis的一级缓存和二级缓存执行顺序

一句话: 缓存执行顺序是: 二级缓存 --> 一级缓存 --> 数据库

在这里插入图片描述

小实验

1.修改MonsterMapperTest.java
这里有debug, 自己debug看看, 很有意思的

//演示:二级缓存->一级缓存->DB执行的顺序
@Test
public void cacheSeqTest() {System.out.println("查询第1次");//DB, 会发出sql, 分析cache hit radio 0.0Monster monster1 = monsterMapper.getMonsterById(7);System.out.println(monster1);//这里我们关闭sqlSession, 一级缓存数据消失//当我们关闭一级缓存的时候, 如果你配置了二级缓存, 那么一级缓存的数据, 会放入到二级缓存sqlSession.close();sqlSession = MyBatisUtils.getSqlSession();monsterMapper = sqlSession.getMapper(MonsterMapper.class);System.out.println("查询第2次");//从二级缓存获取id=7 monster信息, 就不会发出sql, cache hit radio 0.5Monster monster2 = monsterMapper.getMonsterById(7);System.out.println(monster2);System.out.println("查询第3次");//从二级缓存获取id=7 monster信息, 就不会发出sql, cache hit radio 0.666666Monster monster3 = monsterMapper.getMonsterById(7);System.out.println(monster3);if (sqlSession != null) {sqlSession.close();}
}

第一次, 去数据库中查询

在这里插入图片描述

第二次, 从二级缓存中获取

在这里插入图片描述

第三次, 从二级缓存中获取

在这里插入图片描述

细节说明

1.不会出现一级缓存和二级缓存中有同一个数据, 因为二级缓存是在一级缓存关闭之后才有的.

2.修改MonsterMapperTest.java, 不关闭一级缓存, 看看运行效果 这里有debug, 自己debug看看, 很有意思的

//分析缓存执行顺序
//二级缓存->一级缓存->DB
@Test
public void cacheSeqTest2() {System.out.println("查询第1次");//DB, 会发出sql, 分析cache hit radio 0.0Monster monster1 = monsterMapper.getMonsterById(7);System.out.println(monster1);//这里没有关闭sqlSessionSystem.out.println("查询第2次");//从一级缓存获取id=7, cache hit radio 0.0, 不会发出sqlMonster monster2 = monsterMapper.getMonsterById(7);System.out.println(monster2);System.out.println("查询第3次");//还是从一级缓存获取id=7, cache hit radio 0.0, 不会发出sqlMonster monster3 = monsterMapper.getMonsterById(7);System.out.println(monster3);if (sqlSession != null) {sqlSession.close();}
}

第一次, 去数据库中查询

在这里插入图片描述

第二次, 去一级缓存中查询

在这里插入图片描述

第三次, 从一级缓存中查询

在这里插入图片描述

3.运行效果, 可以看到, 在一级缓存存在的情况下, 依然是先查询二级缓存, 但是因为二级缓存, 没有数据, 所以命中率都是 0.0. 可以debug

查询第1次
Cache Hit Ratio [com.zzw.mapper.MonsterMapper]: 0.0

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 462773420.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1b955cac]
==> Preparing: SELECT * FROM monster WHERE id = ?
==> Parameters: 7(Integer)
<== Columns: id, age, birthday, email, gender, name, salary
<== Row: 7, 33, 2024-02-09, 978964140@qq.com, 1, 赵志伟_, 1010.0
<== Total: 1
Monster{id=7, age=33, birthday=Fri Feb 09 00:00:00 CST 2024, email=‘978964140@qq.com’, gender=1, name=‘赵志伟_’, salary=1010.0}
查询第2次
Cache Hit Ratio [com.zzw.mapper.MonsterMapper]: 0.0

Monster{id=7, age=33, birthday=Fri Feb 09 00:00:00 CST 2024, email=‘978964140@qq.com’, gender=1, name=‘赵志伟_’, salary=1010.0}
查询第3次
Cache Hit Ratio [com.zzw.mapper.MonsterMapper]: 0.0

Monster{id=7, age=33, birthday=Fri Feb 09 00:00:00 CST 2024, email=‘978964140@qq.com’, gender=1, name=‘赵志伟_’, salary=1010.0}

EnCache缓存

配置文档: https://www.cnblogs.com/zqyanywn/p/10861103.html

基本介绍

1.EhCache 是一个纯Java的缓存框架, 具有快速, 精干等特点.
2.MyBatis有自己默认的二级缓存(前面我们已经讲过了), 但是在实际项目中, 往往使用的是更加专业的第三方缓存产品, 作为MyBatis的二级缓存, EhCache就是非常优秀的缓存产品.
在这里插入图片描述

配置和使用EhCache

1.引入相关依赖, 修改mybatis_cache\pom.xml

<dependencies><!--引入ehcache核心库/jar--><dependency><groupId>net.sf.ehcache</groupId><artifactId>ehcache-core</artifactId><version>2.6.11</version></dependency><!--引入需要使用的slf4j--><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.36</version></dependency><!--引入mybatis整合ehcache库/jar--><dependency><groupId>org.mybatis.caches</groupId><artifactId>mybatis-ehcache</artifactId><version>1.2.1</version></dependency>
</dependencies>

2.mybatis-config.xml 仍然打开二级缓存

<settings><!--1.全局性地开启或关闭所有映射器配置文件中已配置的任何缓存, 可以理解这是一个总开关2.默认就是: true--><setting name="cacheEnabled" value="true"/>
</settings>

3.加入src/main/resources/ehcache.xml配置文件
- 文档说明: https://www.taobye.com/f/view-11-23.html

<?xml version="1.0" encoding="UTF-8"?>
<ehcache><!--diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。参数解释如下:user.home – 用户主目录user.dir  – 用户当前工作目录java.io.tmpdir – 默认临时文件路径--><diskStore path="java.io.tmpdir/Tmp_EhCache"/><!--defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。--><!--name:缓存名称。maxElementsInMemory:缓存最大数目maxElementsOnDisk:硬盘最大缓存个数。eternal:对象是否永久有效,一但设置了,timeout将不起作用。overflowToDisk:是否保存到磁盘,当系统宕机时timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。clearOnFlush:内存数量最大时是否清除。memoryStoreEvictionPolicy:可选策略(清除策略)有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。FIFO,first in first out,这个是大家最熟的,先进先出。LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。--><defaultCacheeternal="false"maxElementsInMemory="10000"overflowToDisk="false"diskPersistent="false"timeToIdleSeconds="1800"timeToLiveSeconds="259200"memoryStoreEvictionPolicy="LRU"/></ehcache>

4.在XxxMapper.xml中启用EhCache, 当然原来MyBatis自带的缓存配置就注销了

<!--
1.配置二级缓存: 是mybatis自带的二级缓存
2.FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
3.flushInterval 刷新间隔 是毫秒单位 60000, 表示 60s
4.size="512": 引用数目, 属性可以被设置为任意正整数, 默认1024
5.readOnly="true": (只读)属性可以被设置为 true 或 false: 如果我们只是用于读操作, 建议设置成 true, 这样可以提高效率. 如果有修改操作, 设置成 false, 默认就是false
-->
<!--<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>-->
<!--配置/启用ehcache-->
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>

5.修改MonsterMapperTest.java, 增加测试方法, 完成测试+简单 Debug

//测试ehcache缓存的使用
@Test
public void ehCacheTest() {//查询id=7的monsterMonster monster = monsterMapper.getMonsterById(7);//会发出sql,到db查询System.out.println("monster=" + monster);//这里我们关闭sqlSession, 一级缓存[数据]失效. 将数据放入到二级缓存(ehcache)if (sqlSession != null) {sqlSession.close();}//重新获取sqlSessionsqlSession = MyBatisUtils.getSqlSession();//重新获取了monsterMappermonsterMapper = sqlSession.getMapper(MonsterMapper.class);//查询id=7的monsterSystem.out.println("---虽然前面关闭了sqlSession, 因为配置二级缓存[ehcache], " +"当你再次查询相同的id时, 不会再发出sql, 而是从二级缓存[ehcache]获取数据---");Monster monster2 = monsterMapper.getMonsterById(7);System.out.println("monster2=" + monster);//再次查询id=7的monster, 仍然到二级缓存(ehcache), 获取数据, 不会发出sqlMonster monster3 = monsterMapper.getMonsterById(7);System.out.println("monster3=" + monster3);if (sqlSession != null) {sqlSession.close();}
}

细节说明

如何理解EhCacheMyBatis缓存的关系
1.MyBatis提供了一个接口Cache[如右图, 找到org.apache.ibatis.cache, 关联源码包就可以看到Cache接口]

2.只要实现了该Cache接口, 就可以作为二级缓存产品和MyBatis 整合使用, Ehcache 就是实现了该接口

3.MyBatis默认情况(即一级缓存)是使用的PerpetualCache类实现Cache接口的, 是核心类

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

4.当我们使用了Ehcache后, 就是EhcacheCache类实现Cache接口的, 是核心类

在这里插入图片描述

5.我们看一下源码, 发现缓存的本质就是 Map<Object, Object

在这里插入图片描述

MyBatis逆向工程


接下来我们学习, 整合SSM在这里插入图片描述
💐💐💐💐💐💐💐💐给个赞, 点个关注吧, 各位大佬!💐💐💐💐💐💐💐💐

💐💐💐💐💐💐💐💐祝各位2024年大吉大运💐💐💐💐💐💐💐💐💐💐
请添加图片描述

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

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

相关文章

SpringBoot整合Minio(支持公有及私有bucket)

&#x1f60a; 作者&#xff1a; 一恍过去 &#x1f496; 主页&#xff1a; https://blog.csdn.net/zhuocailing3390 &#x1f38a; 社区&#xff1a; Java技术栈交流 &#x1f389; 主题&#xff1a; SpringBoot整合Minio(支持公有及私有bucket) ⏱️ 创作时间&#xff1…

java多版本管理

一 java 多版本管理 使用 jenv, 地址&#xff1a;https://github.com/jenv/jenv mac 操作 brew install jenv# Shell: zsh echo export PATH"$HOME/.jenv/bin:$PATH" >> ~/.zshrc echo eval "$(jenv init -)" >> ~/.zshrc source ~/.zshrc需要…

张大哥笔记:如何选择一个人就值得做的副业

很多人喜欢把上班称为主业&#xff0c;把上班之外的工作称为副业&#xff0c;不管以哪种方式称呼都可以&#xff0c;只要能赚钱就行&#xff0c;上班的本质就是出卖时间&#xff0c;不管你是月入5000还是月入2万&#xff0c;都是给老板打工&#xff01; 但搞笑的就是月入2万的人…

关于app爬虫的环境准备

摘要 有些数据需要在手机应用中才能查看&#xff0c;没有网页版&#xff0c;所以学习移动端的爬虫是有必要的。 手机系统分为安卓和苹果两大系统&#xff0c;本次讲解主要以安卓手机为例 有安卓手机的可以使用手机&#xff0c;没有的可以使用模拟器&#xff0c;本次以夜神模…

基于C++、MFC和Windows套接字实现的简单聊天室程序开发

一、一个简单的聊天室程序 该程序由服务器端和客户端两个项目组成&#xff0c;这两个项目均基于对话框的程序。服务器端项目负责管理客户端的上线、离线状态&#xff0c;以及转发客户端发送的信息。客户端项目则负责向服务器发送信息&#xff0c;并接收来自服务器的信息&#…

YAML 快速上手

文章目录 1.语法2.历史版本3.数据结构对象数组复合结构标量 4.引用5.文本块6.显示指定类型7.单文件多文档8.解析9.完整示例参考文献 YAML&#xff08;YAML Ain’t a Markup Language&#xff09;是专门用来写配置文件的语言&#xff0c;简洁强大&#xff0c;相比于 JSON 和 XML…

R包开发详细教程

开发一个R包可以帮助你组织和共享代码。以下是一个详细的步骤教程&#xff0c;介绍如何开发一个R包。 步骤 1: 准备工作 确保你已经安装了以下R包&#xff1a; install.packages("devtools") install.packages("roxygen2") install.packages("test…

[机器学习算法]决策树

1. 理解决策树的基本概念 决策树是一种监督学习算法&#xff0c;可以用于分类和回归任务。决策树通过一系列规则将数据划分为不同的类别或值。树的每个节点表示一个特征&#xff0c;节点之间的分支表示特征的可能取值&#xff0c;叶节点表示分类或回归结果。 2. 决策树的构建…

基于深度学习的图像压缩

基于深度学习的图像压缩 图像压缩是指将图像数据量减小的同时尽量保留其视觉质量的过程。传统的图像压缩方法&#xff08;如JPEG、PNG等&#xff09;已经广泛应用&#xff0c;但随着深度学习技术的发展&#xff0c;基于深度学习的图像压缩方法逐渐显现出其优越性。以下是一些关…

【AI原理解析】— Kimi模型

目录 一、技术背景与基础 二、核心技术特点 深度学习与神经网络&#xff1a; 超长上下文学习&#xff1a; 多模态对齐&#xff1a; 个性化调优&#xff1a; 知识增强&#xff1a; 推理优化&#xff1a; 三、模型架构 一、技术背景与基础 Kimi模型是一种基于深度学习和…

enum库

Python enum 模块教程 enum 是 Python 3.4 引入的一个模块&#xff0c;用于定义枚举类型。枚举类型是一种特殊的数据类型&#xff0c;由一组命名的值组成&#xff0c;这些值称为枚举成员。使用 enum 可以提高代码的可读性和可维护性&#xff0c;特别是在处理一组相关的常量值时…

Laravel 的事件监听器与服务提供者和服务容器的二三事

一. Laravel 的事件监听器与服务提供者和服务容器有密切的关系。 服务提供者用于注册服务、绑定依赖关系以及执行框架的初始化设置。在服务提供者的 register 方法中&#xff0c;可以注册事件和事件监听器。 服务容器则负责管理对象的创建和依赖注入。事件监听器通常会被注册…

人工智能和机器学习的应用日益广泛,在医疗健康领域的具体应用是什么?

人工智能&#xff08;AI&#xff09;和机器学习&#xff08;ML&#xff09;在医疗健康领域的应用日益广泛&#xff0c;涵盖了从疾病预测、辅助诊断、药物研发到健康管理等多个方面。以下是一些具体的应用实例和成功案例&#xff1a; 疾病预测与辅助诊断&#xff1a;机器学习算…

实现Spring Boot中多数据源配置的解决方案

&#x1f341; 作者&#xff1a;知识浅谈&#xff0c;CSDN签约讲师&#xff0c;CSDN博客专家&#xff0c;华为云云享专家&#xff0c;阿里云专家博主 &#x1f4cc; 擅长领域&#xff1a;全栈工程师、爬虫、ACM算法 &#x1f525; 微信&#xff1a;zsqtcyw 联系我领取学习资料 …

python实现进度条的方法和实现代码

在Python中&#xff0c;有多种方式可以实现进度条。这里&#xff0c;我将介绍七种常见的方法&#xff1a;使用tqdm&#xff08;这是一个外部库&#xff0c;非常流行且易于使用&#xff09;、rich、click、progressbar2等库以及纯Python的print函数与time库来模拟进度条。 目录…

《STM32 HAL库》小米微电机控制例程——通信协议分析及驱动库

之前有段时间因为机器狗项目的缘故&#xff0c;一直在使用小米微电机&#xff0c;但是苦于没有一个详尽的奶妈级教程&#xff0c;在控制电机的学习中踩了不少的坑。今天咱们就从头至尾一步一步的实现使用按键控制小米微电机。本文将会分析小米电机驱动库&#xff0c;并简要介绍…

Spring Boot 项目中的如何序列化日期格式字符串(对象转JSON的日期字符串格式)

在Spring Boot 项目中, 将Bean序列化为一个JSON字符串的时候, 对于日期类型的属性, 可以直接在属性上使用@JsonFormat即可达成, 但是如果属性本身就是一个日期的字符串, 要输出为另外格式字符串要如何实现呢? @JsonFormat 转换 Timestamp 类型的属性 @JsonFormat注…

linux系统中,pwd获取当前路径,dirname获取上一层路径;不使用 ../获取上一层路径

在实际项目中&#xff0c;我们通常可以使用 pwd 来获取当前路径&#xff0c;但是如果需要获取上一层路径&#xff0c;有不想使用 …/ 的方式&#xff0c;可以尝试使用 dirname指令 测试shell脚本 #!/bin/bash# 获取当前路径 CURRENT_PATH$PWD echo "CURRENT_PATH$CURREN…

练手代码之使用Python实现合并PDF文件

如果你有合并PDF的需要&#xff0c;你会怎么办 我们无所不能的程序员会选择写一个Python代码来实现&#xff08;谁会这么无聊&#xff1f;是我&#xff09;&#xff0c;如果真的有PDF操作需要&#xff0c;我推荐你使用PDF Expert这个软件哈~ 话不多说直接上代码&#xff1a; …

【Ruby简单脚本01】查看wifi密码

脚本 # 使用io库 def get_cmd_result(cmd) IO.popen(cmd,:external_encoding>GBK).read.encode("utf-8") end def list_wifi wifi_pwds Hash.new # 获取所有wifi文件 o1 get_cmd_result("netsh wlan show profiles") # 获取所有匹配结果 …