石一歌的Mybatis笔记
mybatis
mybatis-demo
-
pom.xml添加依赖
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.2</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.2</version> </dependency>
-
资源文件夹创建核心配置文件mybatise-config.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核心配置文件--> <configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?userSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> </configuration>
-
编写工具类
//sqlSessionFactory --> sqlSession public class MybatisUtils { static SqlSessionFactory sqlSessionFactory = null; static { try { //使用Mybatis第一步 :获取sqlSessionFactory对象 String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } //既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例. // SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。 public static SqlSession getSqlSession(){ return sqlSessionFactory.openSession(); } }
-
代码调用
Dao
public interface UserDao { public List<User> getUserList(); }
实现类变为Mapper.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"> <!--namespace=绑定一个指定的Dao/Mapper接口--> <mapper namespace="com.kuang.dao.UserDao"> <select id="getUserList" resultType="com.kuang.pojo.User"> select * from USER </select> </mapper>
-
测试
@Test public void test(){ //1.获取SqlSession对象 SqlSession sqlSession = MybatisUtils.getSqlSession(); //2.执行SQL // 方式一:getMapper UserDao userDao = sqlSession.getMapper(UserDao.class); List<User> userList = userDao.getUserList(); for (User user : userList) { System.out.println(user); } //关闭sqlSession sqlSession.close(); }
mapper
-
namespace
namespace中的包名要和Dao/Mapper接口的包名一致
-
select
id:就是对应的namespace中的方法名;
resultType : Sql语句执行的返回值;
parameterType : 参数类型;
注意:增删改一定要提交事务:
sqlSession.commit();
-
万能Map
假设,我们的实体类,或者数据库中的表,字段或者参数过多,我们应该考虑使用Map!
Map传递参数,#{ }直接在sql中取出key即可! 【parameter=“map”】
对象传递参数,#{ }直接在sql中取出对象的属性即可! 【parameter=“Object”】 -
模糊查询
Java代码执行的时候,传递通配符% %
List<User> userList = mapper.getUserLike("%李%");
在sql拼接中使用通配符
select * from user where name like "%"#{value}"%";
mybatise-config
可配置项
configuration(配置)
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
environments
<environments default="development">
<environment id="development-own">
<transactionManager type="JDBC"/>
<!--原生-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?userSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
<environment id="development-druid">
<transactionManager type="JDBC"/>
<!--druid 需要编写工具类-->
<dataSource type="com.nuc.utils.DruidDataSourceFactory">
<property name="driverClassName" value="${jdbcDriver}"/>
<property name="url" value="${jdbcUrl}"/>
<property name="username" value="${jdbcUser}"/>
<property name="password" value="${jdbcPassword}"/>
</dataSource>
</environment>
<environment id="development-c3p0">
<transactionManager type="JDBC"/>
<!--c3p0 需要编写工具类-->
<dataSource type="com.nuc.utils.c3p0DatasourceFactory">
<property name="driverClass" value="${jdbcDriver}"/>
<property name="jdbcUrl" value="${jdbcUrl}"/>
<property name="user" value="${jdbcUser}"/>
<property name="password" value="${jdbcPassword}"/>
<property name="initialPoolSize" value="5"/>
<property name="maxPoolSize" value="20"/>
<property name="minPoolSize" value="5"/>
</dataSource>
</environment>
<environment id="development-dbcp">
<transactionManager type="JDBC"/>
<!--dbcp 需要编写工具类-->
<dataSource type="com.nuc.utils.DbcpDataSourceFactory">
<property name="driverClassName" value="${jdbcDriver}"/>
<property name="url" value="${jdbcUrl}"/>
<property name="username" value="${jdbcUser}"/>
<property name="password" value="${jdbcPassword}"/>
</dataSource>
</environment>
<environment id="development-hikaricp">
<transactionManager type="JDBC"/>
<!--hikaicp 需要编写工具类-->
<dataSource type="com.nuc.utils.HikariDataSourceFactory"/>
</environment>
</environments>
public class c3p0DatasourceFactory extends UnpooledDataSourceFactory{
public c3p0DatasourceFactory() {
this.dataSource=new ComboPooledDataSource();
}
}
public class DruidDataSourceFactory extends UnpooledDataSourceFactory{
public DruidDataSourceFactory() {
this.dataSource=new DruidDataSource();
}
}
public class DbcpDataSourceFactory extends UnpooledDataSourceFactory{
public DruidDataSourceFactory() {
this.dataSource=new PooledDataSource();
}
}
public class HikariDataSourceFactory extends
UnpooledDataSourceFactory {
public HikariDataSourceFactory(){
HikariConfig config = new HikariConfig("hikariPool.properties");
config.setMaximumPoolSize(5);
this.dataSource = new HikariDataSource(config);
}
}
hikariPool.properties
jdbcUrl=jdbc:mysql://localhost:3306/omc?characterEncoding=utf8&serverTimezone=UTC
dataSource.user=remote
dataSource.password=xxxxxx
dataSource.cachePrepStmts=true
dataSource.prepStmtCacheSize=250
dataSource.prepStmtCacheSqlLimit=2048
dataSource.useServerPrepStmts=true
dataSource.useLocalSessionState=true
dataSource.rewriteBatchedStatements=true
dataSource.cacheResultSetMetadata=true
dataSource.cacheServerConfiguration=true
dataSource.elideSetAutoCommits=true
dataSource.maintainTimeStats=false
# 等待连接池分配连接的最大时长(毫秒),超过这个时长还没可用的连接则发生SQLException, 缺省:30秒
dataSource.connectionTimeout=30000
# 一个连接idle状态的最大时长(毫秒),超时则被释放(retired),缺省:10分钟 -->
dataSource.idleTimeout=600000
# 一个连接的生命时长(毫秒),超时而且没被使用则被释放(retired),缺省:30分钟
# 建议设置比数据库超时时长少30秒,参考MySQL wait_timeout参数(show variables like '%timeout%';)
dataSource.maxLifetime=1800000
# 连接池中允许的最大连接数。缺省值:10;推荐的公式:((core_count * 2) + effective_spindle_count)
# core_count CPU的内核数量
# effective_spindle_count is the number of disks in a RAID.就是磁盘列阵中的硬盘数
dataSource.maximumPoolSize=10
properties
<properties resource="db.properties">
<property name="username" value="root"/>
<property name="password" value="root"/>
</properties>
<!--配置文件会覆盖子标签-->
typeAliases
手动指定
<typeAliases>
<typeAlias type="com.kuang.pojo.User" alias="User"/>
</typeAliases>
包扫描(注解会覆盖)
<typeAliases>
<package name="com.kuang.pojo"/>
</typeAliases>
注解
@Alias("author")
mapper
资源路径
<mappers>
<mapper resource="com/kuang/dao/UserMapper.xml"/>
</mappers>
类路径
- 接口和他的Mapper配置文件必须同名
- 接口和他的Mapper配置文件必须在同一个包下
<mappers>
<mapper class="com.kuang.dao.UserMapper"/>
</mappers>
包扫描
<mappers>
<package name="com.kuang.dao"/>
</mappers>
结果集映射
<resultMap id="UserMap" type="User">
<!--column数据库中的字段,property实体类中的属性-->
<result column="id" property="id"></result>
<result column="name" property="name"></result>
<result column="pwd" property="password"></result>
</resultMap>
<select id="getUserList" resultMap="UserMap">
select * from USER
</select>
驼峰命令
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
日志
- SLF4J
- LOG4J 【掌握】
- LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- STDOUT_LOGGING 【掌握】
- NO_LOGGING
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
log4j
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file
#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/rzp.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n
#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sq1.PreparedStatement=DEBUG
使用方法
-
在要使用Log4j的类中,导入包 import org.apache.log4j.Logger;
-
日志对象,参数为当前类的class对象
Logger logger = Logger.getLogger(UserDaoTest.class);
- 日志级别
logger.info("info: 测试log4j");
logger.debug("debug: 测试log4j");
logger.error("error:测试log4j");
分页
-
limit
接口
List<User> getUserByLimit(Map<String,Integer> map);
Mapper.xml
测试
@Test
public void getUserByLimit(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("startIndex",1);
map.put("pageSize",2);
List<User> list = mapper.getUserByLimit(map);
for (User user : list) {
System.out.println(user);
}
}
-
rowbounds
接口
List<User> getUserByRowBounds();
mapper.xml
测试
public void getUserByRowBounds(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
//RowBounds实现
RowBounds rowBounds = new RowBounds(1, 2);
//通过Java代码层面实现分页
List<User> userList = sqlSession.selectList("com.kaung.dao.UserMapper.getUserByRowBounds", null, rowBounds);
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
-
插件
PageHelper
注解
-
注解在接口上实现
public interface UserMapper { @Select("select * from user") List<User> getUsers(); //方法具有多个参数,前必须加@param @Select("select * from user where id=#{id}") User getUserById(@Param("id") int id); @Insert("insert into user(id,name,pwd)values(#{id},#{name},#{password})") int addUser(User user); @Update("update user set name = #{name},pwd = #{password} where id = #{id}") int updateUser(User user); @Delete("delete from user where id = #{tid}") int deleteUser(@Param("tid") int id); }
-
绑定接口
<mappers> <mapper class="com.kuang.dao.UserMapper"/> </mappers>
关于@Param( )注解
- 基本类型的参数或者String类型,需要加上
- 引用类型不需要加
- 如果只有一个基本类型的话,可以忽略,但是建议大家都加上
- 我们在SQL中引用的就是我们这里的@Param()中设定的属性名
一对一
按照查询嵌套处理
<!--
思路:
1. 查询所有的学生信息
2. 根据查询出来的学生的tid寻找特定的老师 (子查询)
-->
<select id="getStudent" resultMap="StudentTeacher">
select * from student
</select>
<resultMap id="StudentTeacher" type="student">
<result property="id" column="id"/>
<result property="name" column="name"/>
<!--对象:association 集合:collection-->
<association property="teacher" column="tid" javaType="teacher" select="getTeacher"/>
</resultMap>
<select id="getTeacher" resultType="teacher">
select * from teacher where id = #{id}
</select>
-
javaType:把sql语句查询出的结果集,封装给某个类的某个对象-- 属性类型(指定实体类中的java返回值类型)
-
select:下一条要执行的sql语句
-
property:注入给一级查询结果实体类的某个属性-- 属性名
-
column:在上一次查询的结果集中,用那些列值去执行下一条sql语句(传参)
按照结果嵌套处理 (简单)
<!--按照结果进行查询-->
<select id="getStudent2" resultMap="StudentTeacher2">
select s.id sid , s.name sname, t.name tname
from student s,teacher t
where s.tid=t.id
</select>
<!--结果封装,将查询出来的列封装到对象属性中-->
<resultMap id="StudentTeacher2" type="student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="teacher">
<result property="name" column="tname"></result>
</association>
</resultMap>
一对多
按照查询嵌套处理
<!--子查询:按查询嵌套查询-->
<select id="getTeacher" resultMap="teacherToStudent">
select id, name from mybatis.teacher where id = #{id}
</select>
<resultMap id="teacherToStudent" type="teacher">
<result property="id" column="id"/>
<result property="name" column="name"/>
<!-- column="id"是teacher表中的id-->
<collection property="students" javaType="List" ofType="student"
column="id" select="getStudentsByTeacherId" />
<!-- List<student> students -->
</resultMap>
<select id="getStudentsByTeacherId" resultType="student">
select * from mybatis.student where tid = #{id}
</select>
- ofType:映射List或某些指定的pojo泛型的类型
按照结果嵌套处理
<!--联表查询:按结果嵌套查询-->
<select id="getTeacher1" resultMap="teacherAndStudent">
select s.id sid,s.name sname,t.name tname,t.id tid
from mybatis.teacher t,mybatis.student s
where s.tid=t.id and t.id =#{id}
</select>
<resultMap id="teacherAndStudent" type="teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<!--集合对象用collection绑定,javaType是返回单个属性,不能返回集合,
返回属性是集合用ofType绑定-->
<collection property="students" ofType="student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="tid"/>
</collection>
</resultMap>
动态SQL
if
<!--if:通过include跳转到使用,缺点是必须写上判断条件成立 where 1=1-->
<select id="queryBlogByIf" parameterType="map" resultType="Blog">
select * from mybatis.blog where 1=1
<include refid="if_title_author_like" />
</select>
<sql id="if_title_author_like">
<if test="title !=null">
and title like #{title}
</if>
<if test="author !=null">
and author like #{author}
</if>
</sql>
<!--if:通过where直接使用/直接使用if判断,但是不推荐。原理:如果test存在,就自动加上where -->
<select id="queryBlogByWhere" parameterType="map" resultType="Blog">
select * from mybatis.blog
<where>
<if test="id !=null">
id like #{id}
</if>
<if test="views !=null">
and views like #{views}
</if>
</where>
</select>
choose
<select id="findActiveBlogLike" resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<choose>
<when test="title != null">
AND title like #{title}
</when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose>
</select>
where
<select id="findActiveBlogLike" resultType="Blog">
SELECT * FROM BLOG
<where>
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</where>
</select>
- 优势:where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
trim
- 使用自定义 trim 元素来定制 where 元素的功能
与< where>等价的< trim>:prefix="WHERE"满足条件,自动添加的字段:prefixOverrides自动忽略的字段,细节:AND |OR 两者后面都包含了一个空格,这是正确sql的书写要点。
<trim prefix="WHERE" prefixOverrides="AND |OR ">
</trim>
set
-
概念:用于动态update语句的叫做 set。set 元素可以用于动态包含需要更新的列,忽略其它不更新的列
等价的trim语句,set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)= 也就是说其实mybatis自动在update中set就给你加上了逗号,但是你自己手写加上了,< set> 也会给你忽略掉
<trim prefix="SET" suffixOverrides=",">
</trim>
<update id="updateAuthorIfNecessary">
update Author
<set>
<if test="username != null">username=#{username},</if>
<if test="password != null">password=#{password},</if>
<if test="email != null">email=#{email},</if>
<if test="bio != null">bio=#{bio}</if>
</set>
where id=#{id}
</update>
foreach
- 概念:动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)
- 原生:
SELECT * FROM blog WHERE 1=1 AND (id=1 OR id=2);
- 细节:if < where> 多个条件成立时,就会忽略掉原生中and书写
<select id="queryBlogByForeach" parameterType="map" resultType="Blog">
select * from mybatis.blog
<where>
<foreach collection="ids" item="id" open="(" separator="or" close=")">
id=#{id}
</foreach>
</where>
</select>
<!--官网案例:使用in时-->
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT *
FROM POST P
WHERE ID in
<foreach item="item" index="index" collection="list"
open="(" separator="," close=")">
#{item}
</foreach>
</select>
SQL片段
- 使用:< sql id= > 标签和< include refid= >引用
- 细节:
- 最好基于单表使用sql片段,多表别的表不一定支持
- 使用sql片段复用,不要使用< where >标签,因为它内置了会忽略掉某些字段
<sql id="if_title_author_like">
<if test="title !=null">
and title like #{title}
</if>
<if test="author !=null">
and author like #{author}
</if>
</sql>
<select id="queryBlogByIf" parameterType="map" resultType="Blog">
select * from mybatis.blog where 1=1
<include refid="if_title_author_like" />
</select>
缓存
一级缓存
-
默认情况下,只启用了本地的会话(一级)缓存,它仅仅对一个会话中的数据进行缓存。
- 把一级缓存想象成一个会话中的map,便于理解
-
缓存失效
- 增删改会把所有的sql缓存失效,下次会重写从数据库中查
- 查询不同的东西,查询不同的mapper.xml
- 手动清除缓存:sqlSession.clearCache();
二级缓存
mybatis-config,xml开启全局缓存
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!--开启全局缓存 默认是开启的,显示写便于可读-->
<setting name="cacheEnabled" value="true"/>
</settings>
mappper.xml开启二级缓存
<!--开启二级缓存-->
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
<!--eviction:清楚算法,默认LRU -->
<!--flushInterval:刷新缓存间隔,默认无-->
<!--size:最多缓存数量,默认1024-->
<!--readOnly:只读缓存;写操作会不走缓存,直接从数据库查询,默认是读/写缓存-->
-
映射语句文件中的所有 select 语句的结果将会被缓存。
-
映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
-
缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
-
缓存不会定时进行刷新(也就是说,没有刷新间隔)。
-
缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
-
缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
读写缓存需要pojo开启序列化操作
@Data @AllArgsConstructor @NoArgsConstructor public class User implements Serializable { private int id; private String name; private String pwd; public static void main(String[] args) { new ArrayList<>(); new HashMap<>(); new LinkedList<>(); } }
自定义缓存
- 概念:ehcache是一个分布式缓存,主要面向通用缓存
- 手写或者导入第三方的ehcache缓存依赖
- 所以可以自定义ehcache.xml配置文件
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.2.0</version>
</dependency>
- 在mapper.xml中配置
<cache type="org.mybatis.caches.encache.EhcacheCache"/>
- 在resource中创建ehcache.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<defaultCache
eternal="false"
maxElementsInMemory="10000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="259200"
memoryStoreEvictionPolicy="LRU"/>
<cache
name="cloud_user"
eternal="false"
maxElementsInMemory="5000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="1800"
memoryStoreEvictionPolicy="LRU"/>
<!--
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,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
-->
</ehcache>
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!