【Mybatis】14 缓存
1、什么是缓存?
- 缓存是指把经常需要读写的数据,保存到一个高速的缓冲区中,这个行为叫缓存
- 也可以是指被保存在高速缓冲区的数据,也叫缓存
2、Mybatis缓存
Mybatis中分为一级缓存和二级缓存
- 一级缓存,数据缓存在这个SqlSession的作用范围内
- 二级缓存,数据缓存在这个SqlSesssionFactory的作用范围内
一级缓存:
一级缓存是默认开启的,那么如何证实是开启的呢?
同一个SQL语句只会执行一次,并留下缓存,
如果在这个SqlSession存在的期间,再次调用,那么Mybatis将不会执行SQL
而是直接调用缓存执行
案例:
映射接口
- User getUserById(Integer id);
映射器
- <select id="getUserById" resultType="user" parameterType="int">
- SELECT *
- FROM t_user
- WHERE id = #{id}
- </select>
测试类
- @Test
- public void getUserById(){
- SqlSession sqlSession = MybatisUtil.getSqlSession(true);
- UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
- User user1 = userMapper.getUserById(1);
- System.out.println(user1);
- User user2 = userMapper.getUserById(1);
- System.out.println(user2);
- User user3 = userMapper.getUserById(1);
- System.out.println(user3);
- User user4 = userMapper.getUserById(1);
- System.out.println(user4);
- sqlSession.close();
- }
测试结果:
- [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Opening JDBC Connection
- [org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 336371513.
- [cn.dai.mapper.UserMapper.getUserById]-==> Preparing: SELECT * FROM t_user WHERE id = ?
- [cn.dai.mapper.UserMapper.getUserById]-==> Parameters: 1(Integer)
- [cn.dai.mapper.UserMapper.getUserById]-<== Total: 1
- User(id=1, last_name=阿伟, gender=0)
- User(id=1, last_name=阿伟, gender=0)
- User(id=1, last_name=阿伟, gender=0)
- User(id=1, last_name=阿伟, gender=0)
- [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@140c9f39]
- [org.apache.ibatis.datasource.pooled.PooledDataSource]-Returned connection 336371513 to pool.
- Process finished with exit code 0
这里可以看到SQL语句只执行了一次
4次查询只执行了一次,这证明了缓存的存在
也就是说,实际上缓存存放的数据是首次查询出来的一个结果
如果我们反复调用相同的结果,Mybatis就会从缓存中返回数据给我们
但是在查询不同情况下的值的时候,Mybatis还是无法调用缓存来完成
例如我们这样查询不同的数据出来:
- @Test
- public void getUserById(){
- SqlSession sqlSession = MybatisUtil.getSqlSession(true);
- UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
- User user1 = userMapper.getUserById(1);
- System.out.println(user1);
- User user2 = userMapper.getUserById(2);
- System.out.println(user2);
- User user3 = userMapper.getUserById(3);
- System.out.println(user3);
- User user4 = userMapper.getUserById(4);
- System.out.println(user4);
- sqlSession.close();
- }
结果就是不会触发缓存,因为每次查询的都不一样
- [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Opening JDBC Connection
- [org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 336371513.
- [cn.dai.mapper.UserMapper.getUserById]-==> Preparing: SELECT * FROM t_user WHERE id = ?
- [cn.dai.mapper.UserMapper.getUserById]-==> Parameters: 1(Integer)
- [cn.dai.mapper.UserMapper.getUserById]-<== Total: 1
- User(id=1, last_name=阿伟, gender=0)
- [cn.dai.mapper.UserMapper.getUserById]-==> Preparing: SELECT * FROM t_user WHERE id = ?
- [cn.dai.mapper.UserMapper.getUserById]-==> Parameters: 2(Integer)
- [cn.dai.mapper.UserMapper.getUserById]-<== Total: 1
- User(id=2, last_name=阿伟, gender=1)
- [cn.dai.mapper.UserMapper.getUserById]-==> Preparing: SELECT * FROM t_user WHERE id = ?
- [cn.dai.mapper.UserMapper.getUserById]-==> Parameters: 3(Integer)
- [cn.dai.mapper.UserMapper.getUserById]-<== Total: 1
- User(id=3, last_name=杰哥, gender=0)
- [cn.dai.mapper.UserMapper.getUserById]-==> Preparing: SELECT * FROM t_user WHERE id = ?
- [cn.dai.mapper.UserMapper.getUserById]-==> Parameters: 4(Integer)
- [cn.dai.mapper.UserMapper.getUserById]-<== Total: 1
- User(id=4, last_name=阿强, gender=0)
- [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@140c9f39]
- [org.apache.ibatis.datasource.pooled.PooledDataSource]-Returned connection 336371513 to pool.
- Process finished with exit code 0
原理示意:
一级缓存失败的四种情况:
- 不在同一个SqlSession对象中【同一个SQL语句】
- 执行的语句的参数不一样,缓存中也不存在数据【就是上面的演示】
- 执行增、删、改、会清除缓存
- 自行手动清除
手动清除是指SqlSession调用清除缓存方法
- sqlSession.clearCache();
为什么增、删、改、也会清除缓存?
是因为底层在SQL执行完默认就调用了这个方法清除了
二级缓存:
首先,二级缓存默认是不开启的,我们需要在Mybatis的核心配置文件中
配置关于二级缓存的SETTINGS选项,和在映射器的配置文件中加入cache标签
并且,需要被二级缓存的对象,必须要实现序列化接口
示意图:
开启二级缓存的配置操作:
1、核心配置中添加二级缓存配置
2、映射器加入cache标签
3、被缓存的对象所属类必须实现序列化接口
二级缓存开启配置
- <setting name="cacheEnabled" value="true"/>
映射器配置cache标签
- <cache/>
测试类
- public void cacheTest(){
- SqlSession sqlSession = MybatisUtil.getSqlSession(true);
- UserMapper mapper = sqlSession.getMapper(UserMapper.class);
- User userById = mapper.getUserById(1);
- System.out.println(userById);
- sqlSession.close();
- }
- @Test
- public void sync(){
- cacheTest();
- cacheTest();
- }
如果不实现序列化接口,二级缓存在调用时,就会出现未序列化异常
- [cn.dai.mapper.UserMapper]-Cache Hit Ratio [cn.dai.mapper.UserMapper]: 0.0
- [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Opening JDBC Connection
- [org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 1025309396.
- [cn.dai.mapper.UserMapper.getUserById]-==> Preparing: SELECT * FROM t_user WHERE id = ?
- [cn.dai.mapper.UserMapper.getUserById]-==> Parameters: 1(Integer)
- [cn.dai.mapper.UserMapper.getUserById]-<== Total: 1
- User(id=1, last_name=阿伟, gender=0)
- [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@3d1cfad4]
- [org.apache.ibatis.datasource.pooled.PooledDataSource]-Returned connection 1025309396 to pool.
- org.apache.ibatis.cache.CacheException: Error serializing object. Cause: java.io.NotSerializableException: cn.dai.pojo.User
- at org.apache.ibatis.cache.decorators.SerializedCache.serialize(SerializedCache.java:94)
- at org.apache.ibatis.cache.decorators.SerializedCache.putObject(SerializedCache.java:55)
- at org.apache.ibatis.cache.decorators.LoggingCache.putObject(LoggingCache.java:49)
- at org.apache.ibatis.cache.decorators.SynchronizedCache.putObject(SynchronizedCache.java:43)
- at org.apache.ibatis.cache.decorators.TransactionalCache.flushPendingEntries(TransactionalCache.java:116)
- at org.apache.ibatis.cache.decorators.TransactionalCache.commit(TransactionalCache.java:99)
- at org.apache.ibatis.cache.TransactionalCacheManager.commit(TransactionalCacheManager.java:44)
- at org.apache.ibatis.executor.CachingExecutor.close(CachingExecutor.java:61)
- at org.apache.ibatis.session.defaults.DefaultSqlSession.close(DefaultSqlSession.java:263)
- at BuildTest.cacheTest(BuildTest.java:59)
- at BuildTest.sync(BuildTest.java:64)
- at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
- at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
- at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
- at java.lang.reflect.Method.invoke(Method.java:498)
- at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
- at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
- at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
- at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
- at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
- at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
- at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
- at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
- at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
- at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
- at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
- at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
- at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
- at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
- at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
- at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
- at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
- at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
- at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
- at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
- at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
- Caused by: java.io.NotSerializableException: cn.dai.pojo.User
- at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
- at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
- at java.util.ArrayList.writeObject(ArrayList.java:766)
- at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
- at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
- at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
- at java.lang.reflect.Method.invoke(Method.java:498)
- at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:1140)
- at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496)
- at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
- at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
- at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
- at org.apache.ibatis.cache.decorators.SerializedCache.serialize(SerializedCache.java:90)
- ... 35 more
- Process finished with exit code -1
所以需要我们自己来把实体类序列化
再次测试:
- [cn.dai.mapper.UserMapper]-Cache Hit Ratio [cn.dai.mapper.UserMapper]: 0.0
- [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Opening JDBC Connection
- [org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 1025309396.
- [cn.dai.mapper.UserMapper.getUserById]-==> Preparing: SELECT * FROM t_user WHERE id = ?
- [cn.dai.mapper.UserMapper.getUserById]-==> Parameters: 1(Integer)
- [cn.dai.mapper.UserMapper.getUserById]-<== Total: 1
- User(id=1, last_name=阿伟, gender=0)
- [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@3d1cfad4]
- [org.apache.ibatis.datasource.pooled.PooledDataSource]-Returned connection 1025309396 to pool.
- [cn.dai.mapper.UserMapper]-Cache Hit Ratio [cn.dai.mapper.UserMapper]: 0.5
- User(id=1, last_name=阿伟, gender=0)
- Process finished with exit code 0
可以看到二次调用时不再调用SQL查询,而是使用了二级缓存保留的数据返回结果
一些说明:
useCache属性,这个属性是放在SQL查询标签中的<SELECT>
默认TRUE(就是不写也表示开启的),表示使用二级缓存,前提是二级缓存是开启的
在上面的全局测试中已经演示了结果
如果更改为False就是取消这个SQL的二级缓存
测试结果就是第二次查询就需要再次调用SQL了
- [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Opening JDBC Connection
- [org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 1504642150.
- [cn.dai.mapper.UserMapper.getUserById]-==> Preparing: SELECT * FROM t_user WHERE id = ?
- [cn.dai.mapper.UserMapper.getUserById]-==> Parameters: 1(Integer)
- [cn.dai.mapper.UserMapper.getUserById]-<== Total: 1
- User(id=1, last_name=阿伟, gender=0)
- [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@59af0466]
- [org.apache.ibatis.datasource.pooled.PooledDataSource]-Returned connection 1504642150 to pool.
- [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Opening JDBC Connection
- [org.apache.ibatis.datasource.pooled.PooledDataSource]-Checked out connection 1504642150 from pool.
- [cn.dai.mapper.UserMapper.getUserById]-==> Preparing: SELECT * FROM t_user WHERE id = ?
- [cn.dai.mapper.UserMapper.getUserById]-==> Parameters: 1(Integer)
- [cn.dai.mapper.UserMapper.getUserById]-<== Total: 1
- User(id=1, last_name=阿伟, gender=0)
- [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@59af0466]
- [org.apache.ibatis.datasource.pooled.PooledDataSource]-Returned connection 1504642150 to pool.
- Process finished with exit code 0
flushCache属性,这个属性是放在增、删、改、SQL语句中,
表示自动清除缓存,默认值TRUE,另外不要手贱改FALSE
如果不清除缓存,二次调用就从缓存的数据进行返回
为什么这么说?
- 先查询一个结果
【主键:01,名字:阿伟,性别:男】
- 不清除缓存进行修改记录,变更为
【主键:01,名字:杰哥,性别:男】
- 当二次查询时,Mybatis不会再调用SQL重新查询,
直接跑到缓存中返回数据,这个查询返回的结果就是
【主键:01,名字:阿伟,性别:男】
但实际上数据库已经更改,这样查询返回的结果是不对的
所以不要修改flushCache属性为False!!!
3、Cache标签:
当你在映射器中标注了此标签,Mybatis会默认开启这些功能:
- 映射语句文件中的所有 select 语句的结果将会被缓存。
- 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
- 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
- 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
- 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。【就是能放多少个缓存】
- 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
1、最近最少使用算法
【Least Recently Used 】LRU算法来清除不需要的缓存。
移除符合这个描述的对象,优化内存空间
除此之外还有其他算法,这个属性值由eviction配置
2、先进先出算法
FIFO,First In First Out 先进先出
按对象进入缓存的顺序来移除
3、软引用算法
SOFT,移除基于GC回收状态和软引用规则的对象
4、弱引用算法
WEAK,弱引用,更积极的移除基于GC回收状态和弱引用规则的对象
当然,默认使用的是LRU最少使用原则清除
- LRU
- FIFO
- SOFT
- WEAK
二、可读可写的说明:
- readOnly="true"
可读是共享对象的,所有的SqlSession如果需要调用这个二级缓存
指针就会直接引用缓存返回对象
可读是非共享的,所有SqlSession如果调用二级缓存,
那么Mybatis会分别new一个对象,并把缓存对象的属性值赋值给这些new出来的对象
指针则引用这些对象进写入修改
三、自定义二级缓存
可以通过实现你自己的缓存,或为其他第三方缓存方案创建适配器,来完全覆盖缓存行为。
- <cache type="com.domain.something.MyCustomCache"/>
我们可以查看Mybatis的缓存实现类是怎么写的
- package org.apache.ibatis.cache.impl;
- import java.util.HashMap;
- import java.util.Map;
- import org.apache.ibatis.cache.Cache;
- import org.apache.ibatis.cache.CacheException;
- public class PerpetualCache implements Cache {
- private final String id;
- private final Map<Object, Object> cache = new HashMap();
- public PerpetualCache(String id) {
- this.id = id;
- }
- public String getId() {
- return this.id;
- }
- public int getSize() {
- return this.cache.size();
- }
- public void putObject(Object key, Object value) {
- this.cache.put(key, value);
- }
- public Object getObject(Object key) {
- return this.cache.get(key);
- }
- public Object removeObject(Object key) {
- return this.cache.remove(key);
- }
- public void clear() {
- this.cache.clear();
- }
- public boolean equals(Object o) {
- if (this.getId() == null) {
- throw new CacheException("Cache instances require an ID.");
- } else if (this == o) {
- return true;
- } else if (!(o instanceof Cache)) {
- return false;
- } else {
- Cache otherCache = (Cache)o;
- return this.getId().equals(otherCache.getId());
- }
- }
- public int hashCode() {
- if (this.getId() == null) {
- throw new CacheException("Cache instances require an ID.");
- } else {
- return this.getId().hashCode();
- }
- }
- }
四、缓存的执行顺序
1、当我们执行一个查询语句的时候,mybatis会先去二级缓存中查询数据,如果二级缓存中没有,就到一级缓存中查找
2、如果一级缓存也没有,调用SQL执行
3、执行返回,并且结果保存进一级缓存
4、SqlSession关闭,一级缓存保存到二级缓存中
【Mybatis】14 缓存的更多相关文章
- 八 mybatis查询缓存(一级缓存,二级缓存)和ehcache整合
1 查询缓存 1.1 什么是查询缓存 mybatis提供查询缓存,用于减轻数据压力,提高数据库性能. mybaits提供一级缓存,和二级缓存.
- MyBatis一级缓存引起的无穷递归
MyBatis一级缓存引起的无穷递归 引言: 最近在项目中参与了一个领取优惠劵的活动,当多个用户领取同一张优惠劵的时候,使用了数据库锁控制并发,起初的设想是:如果多个人同时领一张劵,第一个到达的人领取 ...
- 【MyBatis源码解析】MyBatis一二级缓存
MyBatis缓存 我们知道,频繁的数据库操作是非常耗费性能的(主要是因为对于DB而言,数据是持久化在磁盘中的,因此查询操作需要通过IO,IO操作速度相比内存操作速度慢了好几个量级),尤其是对于一些相 ...
- mybatis学习--缓存(一级和二级缓存)
声明:学习摘要! MyBatis缓存 我们知道,频繁的数据库操作是非常耗费性能的(主要是因为对于DB而言,数据是持久化在磁盘中的,因此查询操作需要通过IO,IO操作速度相比内存操作速度慢了好几个量级) ...
- 三)mybatis 二级缓存,整合ehcache
mybatis-config.xml <setting name="cacheEnabled" value="true" /> PersonMapp ...
- MyBatis 示例-缓存
MyBatis 提供两种类型的缓存,一种是一级缓存,另一种是二级缓存,本章通过例子的形式描述 MyBatis 缓存的使用. 测试类:com.yjw.demo.CacheTest 一级缓存 MyBati ...
- MyBatis框架——缓存机制
使⽤缓存机制的作⽤也是减少 Java 应⽤程序与数据库的交互次数,从⽽提升程序的运⾏效率. ⽐如第 ⼀次查询出某个对象之后,MyBatis 会⾃动将其存⼊缓存,当下⼀次查询同⼀个对象时,就可以直接从 ...
- [.net 面向对象程序设计进阶] (14) 缓存(Cache) (一) 认识缓存技术
[.net 面向对象程序设计进阶] (14) 缓存(Cache)(一) 认识缓存技术 本节导读: 缓存(Cache)是一种用空间换时间的技术,在.NET程序设计中合理利用,可以极大的提高程序的运行效率 ...
- 通过源码分析MyBatis的缓存
前方高能! 本文内容有点多,通过实际测试例子+源码分析的方式解剖MyBatis缓存的概念,对这方面有兴趣的小伙伴请继续看下去~ MyBatis缓存介绍 首先看一段wiki上关于MyBatis缓存的介绍 ...
- MyBatis 一级缓存与二级缓存
MyBatis一级缓存 MyBatis一级缓存默认开启,一级缓存为Session级别的缓存,在执行以下操作时一级缓存会清空 1.执行session.clearCache(); 2.执行CUD操作 3. ...
随机推荐
- RabbbitMQ RabbitListener使用IP动态队列 Attribute value must be constant
在RabbitMQ消息队列使用 @RabbitListener 接收消息,队列名称使用常量命名,但是如果使用动态队列名称,比如根据系统 ip 命名队列名称. 获取服务器 IP /** * 获取服务器i ...
- idea mapper xml 文件报红
在使用 idea 打开 mapper 文件,出现一下报红错误: 可以看到数据表和字段都是红色的. 解决方案 打开设置,window版本是打开Settings: 找到 Languages & F ...
- C#.NET HTTPS 双向证书 请求被中止: 未能创建 SSL/TLS 安全通道。
请求被中止: 未能创建 SSL/TLS 安全通道. 用mmc 给私钥证书添加Everyone 的权限.
- 使用Git命令从本地上传到码云
Gitee创建仓库内没有内容 本地: 初始化Git仓库:git init 提交文件到暂存区:git add . //. 表示提交所有文件 提交文件到工作区:git commit -m "此次 ...
- python重拾基础第一天
本节内容 Python介绍 发展史 Python 2 or 3? 安装 Hello World程序 变量 用户输入 模块初识 .pyc是个什么鬼? 数据类型初识 数据运算 表达式if ...else语 ...
- P2467 [SDOI2010] 地精部落 学习笔记
DP 显然我固定第一个是峰,然后再乘以2就是答案,因为一个合法的反转之后也是合法的而且谷峰颠倒了 发现如果设\(dp[i][j]\)表示前\(i\)个山脉,第\(i\)个山脉是高度\(j\)的答案,然 ...
- Android日志系统(logging system)
Android日志系统(logging system) 背景 不管是做Android应用还是做Android中间层和底层,在做一些调试工作的时候,使用adb logcat非常关键.特意学习了一下安卓的 ...
- (Java)常用类库
Spring 常用工具类 Spring作为常用的开发框架,在Spring框架应用中,排在ApacheCommon.Guava.Huool等通用库后,第二优先级可以考虑使用Spring-core-xxx ...
- debian12 笔记
前言 最近在win10通过wsl安装了debian linux子系统(wsl2安装报错了..所以改成了wsl),没想到安装的还是最新的debian12 (Bookworm).的确和ubuntu有些不一 ...
- weui weui-switch 开关取值,设置默认状态
html <div class="weui-cell__ft"> <input class="weui-switch" type=" ...