MyBatis缓存-一级缓存--二级缓存的非常详细的介绍

news2024/11/25 18:41:16

目录

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

缓存-官方文档

一级缓存

基本说明

一级缓存原理图

代码演示

修改MonsterMapperTest.java, 增加测试方法

结果 

 debug 一级缓存执行流程

 一级缓存失效分析

关闭sqlSession会话后 , 一级缓存失效

如果执行sqlSession.clearCache() , 会导致一级缓存失效

如果修改了同一个对象 , 会导致一级缓存[对象数据]失效

MyBatis--之二级缓存

基本介绍

示意图

 二级缓存快速入门

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

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

 解读

name:缓存名称。

修改 MonsterMapperTest.java , 完成测试

语句发送的顺序:

语句返回的顺序:

注意事项和使用陷阱 

理解二级缓存策略的参数

上面的配置意思如下:

 四大策略

 如何禁用二级缓存

MonsterMapper.xml 

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

mybatis 刷新二级缓存的设置 

注意

EhCache 缓存-细节说明  


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

缓存-官方文档

文档地址: https://mybatis.org/mybatis-3/zh/sqlmap-xml.html#cache

一级缓存

基本说明

1.     默认情况下,mybatis 是启用一级缓存的/本地缓存/local Cache,它是 SqlSession 级别的。

2.     同一个 SqlSession 接口对象调用了相同的 select 语句,会直接从缓存里面获取,而不是再去查询数据库

一级缓存原理图

代码演示

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

创建新module: mybatis_cache , 必要的文件和配置直接从mybatis_quickstart module拷贝即可

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

修改MonsterMapperTest.java, 增加测试方法

测试一级缓存的基本使用

public class MonsterMapperTest {
    //属性
    private SqlSession sqlSession;
    private MonsterMapper monsterMapper;

    /**
     * 解读
     * 1. 当方法标注 @Before, 表示在执行你的目标测试方法前,会先执行该方法
     * 2. 这里在测试的时候,可能小伙伴们会遇到一些麻烦,老师说了解决方案
     */
    //编写方法完成初始化
    @Before
    public void init() {
        //获取到sqlSession
        sqlSession = MyBatisUtils.getSqlSession();
        //获取到到MonsterMapper对象 class com.sun.proxy.$Proxy7 代理对象
        //, 底层是使用了动态代理机制, 后面我们自己实现mybatis底层机制时,会讲到
        monsterMapper = sqlSession.getMapper(MonsterMapper.class);
        System.out.println("monsterMapper=" + monsterMapper.getClass());

    }


    @Test
    public void getMonsterById() {

        Monster monster = monsterMapper.getMonsterById(3);
        System.out.println("monster=" + monster);

        if (sqlSession != null) {
            sqlSession.close();
        }
        System.out.println("查询成功~~~~");
    }
    //测试一级缓存
    @Test
    public void level1CacheTest() {

        //查询id=3的monster
        Monster monster = monsterMapper.getMonsterById(3);
        System.out.println("monster=" + monster);
        monsterMapper.getMonsterById(8);


        //再次查询id=3的monster
        //当我们再次查询 id=3的Monster时,直接从一级缓存获取,不会再次发出sql
        System.out.println("--一级缓存默认是打开的,当你再次查询相同的id时, 不会再发出sql----");
        Monster monster2 = monsterMapper.getMonsterById(3);
        System.out.println("monster2=" + monster2);

        if (sqlSession != null) {
            sqlSession.close();
        }
    }
}

结果 

 debug 一级缓存执行流程

 一级缓存失效分析

1. 关闭sqlSession 会话后, 再次查询,会到数据库查询, 修改MonsterMapperTest.java,

关闭sqlSession会话后 , 一级缓存失效

    //测试一级缓存,失效
    //关闭sqlSession会话后 , 一级缓存失效
    @Test
    public void level1CacheTest2() {

        //查询id=3的monster
        Monster monster = monsterMapper.getMonsterById(3);
        System.out.println("monster=" + monster);

        //关闭sqlSession, 一级缓存失效
        if (sqlSession != null) {
            sqlSession.close();
        }

        //因为关闭了sqlSession,所以需要重新初始化sqlSession和 monsterMapper
        sqlSession = MyBatisUtils.getSqlSession();
        monsterMapper = sqlSession.getMapper(MonsterMapper.class);
        //再次查询id=3的monster
        System.out.println("--如果你关闭了sqlSession,当你再次查询相同的id时, 仍然会发出sql----");
        Monster monster2 = monsterMapper.getMonsterById(3);
        System.out.println("monster2=" + monster2);

        if (sqlSession != null) {
            sqlSession.close();
        }
    }

如果执行sqlSession.clearCache() , 会导致一级缓存失效

 @Test
    public void level1CacheTest3() {

        //查询id=3的monster
        Monster monster = monsterMapper.getMonsterById(3);
        System.out.println("monster=" + monster);


        //执行clearCache
        /**
         * @Override
         *   public void clearCache() {
         *     executor.clearLocalCache();
         *   }
         */
        sqlSession.clearCache();

        //再次查询id=3的monster
        System.out.println("--如果你执行sqlSession.clearCache(),当你再次查询相同的id时, 仍然会发出sql----");
        Monster monster2 = monsterMapper.getMonsterById(3);
        System.out.println("monster2=" + monster2);


        if (sqlSession != null) {
            sqlSession.close();
        }
    }

如果修改了同一个对象 , 会导致一级缓存[对象数据]失效

 @Test
    public void level1CacheTest4() {

        //查询id=3的monster
        Monster monster = monsterMapper.getMonsterById(3);
        System.out.println("monster=" + monster);


        //如果修改了同一个对象 , 会导致一级缓存[对象数据]失效
        monster.setName("蚂蚱精");
        monsterMapper.updateMonster(monster);

        //再次查询id=3的monster
        System.out.println("--如果你修改了同一个对象,当你再次查询相同的id时, 仍然会发出sql----");
        Monster monster2 = monsterMapper.getMonsterById(3);
        System.out.println("monster2=" + monster2);


        if (sqlSession != null) {
            sqlSession.commit();//这里需要commit
            sqlSession.close();
        }
    }


MyBatis--之二级缓存

基本介绍

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

示意图

 二级缓存快速入门

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

    <!--配置MyBatis自带的日志输出-查看原生的sql-->
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
        <!--
        1、全局性地开启或关闭所有映射器配置文件中已配置的任何缓存, 可以理解这是一个总开关
        2、默认就是: true
        -->
        <!--开启二级缓存-->
        <setting name="cacheEnabled" value="true"/>
    </settings>

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

 

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

 如我这里是ehcache.xml.

<?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,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
   -->
    <defaultCache
            eternal="false"
            maxElementsInMemory="10000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="1800"
            timeToLiveSeconds="259200"
            memoryStoreEvictionPolicy="LRU"/>

</ehcache>

 解读

diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。参数

解释如下:    

user.home – 用户主目录.   

user.dir  – 用户当前工作目录.

java.io.tmpdir – 默认临时文件路径.

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,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存

修改 MonsterMapperTest.java , 完成测试

@Test
    public void level2CacheTest() {

        //查询id=3的monster
        Monster monster = monsterMapper.getMonsterById(3);
        //会发出SQL, 到db查询
        System.out.println("monster=" + monster);

        //这里关闭sqlSession
        if (sqlSession != null) {
            sqlSession.close();
        }

        //重新获取sqlSession
        sqlSession = MyBatisUtils.getSqlSession();
        //重新获取了monsterMapper
        monsterMapper = sqlSession.getMapper(MonsterMapper.class);
        //再次查询id=3的monster
        System.out.println("--虽然前面关闭了sqlSession,因为配置二级缓存, " +
                "当你再次查询相同的id时, 依然不会再发出sql, 而是从二级缓存获取数据----");
        Monster monster2 = monsterMapper.getMonsterById(3);
        System.out.println("monster2=" + monster2);

        Monster monster3 = monsterMapper.getMonsterById(3);
        System.out.println("monster3=" + monster3);

        if (sqlSession != null) {
            sqlSession.close();
        }
    }

语句发送的顺序:

第一步 先去二级缓存寻找去寻找对比有就返回-->

第二步在去一级缓存去寻找对比有就返回--->

第3步再去发送sql语句去找需要的数据

语句返回的顺序:

第二步的好处:可以及时更新缓存 然后防止缓存数据出错

第一步数据库返回需要的数据-->

第二步保存或者跟新在一级缓存里面的数据-->

第三步有一级缓存发送给用户也就是是输出-->

第四步 关闭程序或者关闭sqlSession会话后  或者   执行sqlSession.clearCache()    之前判断是否开启了二级缓存是的话就把数据保存在二级缓存如果没有开启的话就直接清除 

   //分析缓存执行顺序
    //二级缓存->一级缓存->DB
    //因为二级缓存(数据)是在一级缓存关闭之后才有的
    @Test
    public void cacheSeqTest2() {

        System.out.println("查询第1次");

        //DB , 会发出 SQL, cache hit ratio 0.0
        Monster monster1 = monsterMapper.getMonsterById(3);
        System.out.println(monster1);

        //这里我们没有关闭sqlSession

        System.out.println("查询第2次");
        //从一级缓存获取id=3 , cache hit ratio 0.0, 不会发出SQL
        Monster monster2 = monsterMapper.getMonsterById(3);
        System.out.println(monster2);

        System.out.println("查询第3次");
        //还是从一级缓存获取id=3, cache hit ratio 0.0, 不会发出SQL
        Monster monster3 = monsterMapper.getMonsterById(3);
        System.out.println(monster3);

        if (sqlSession != null) {
            sqlSession.commit();
            sqlSession.close();
        }
        System.out.println("操作成功");

    }

注意事项和使用陷阱 

理解二级缓存策略的参数

<cache eviction="FIFO" flushInterval="30000" size="360" readOnly="true"/>

上面的配置意思如下:

创建了 FIFO 的策略,每隔 30 秒刷新一次,最多存放 360 个对象而且返回的对象被认为是只读的。

eviction:缓存的回收策略

flushInterval:时间间隔,单位是毫秒

size:引用数目,内存大就多配置点,要记住你缓存的对象数目和你运行环境的可用内存资源数目。默认值是 1024

readOnly:true,只读

 四大策略

√    LRU – 最近最少使用的:移除最长时间不被使用的对象,它是默认

√    FIFO – 先进先出:按对象进入缓存的顺序来移除它们。

√    SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。

√    WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。

 如何禁用二级缓存



    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
        <!--
        <!--全局性地开启或关闭所有映射器配置文件中已配置的任何缓存, 默认就是 true--> 
        <!--关闭二级缓存-->
        <setting name="cacheEnabled" value="false"/>
    </settings>

MonsterMapper.xml 

<cache eviction="FIFO" flushInterval="30000" size="360" readOnly="true"/>

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

设置 useCache=false 可以禁用当前 select 语句的二级缓存,即每次查询都会发出 sql 去查询, 

默认情况是 true,即该 sql 使用二级缓存

注意:一般我们不需要去修改,使用默认的即可

mybatis 刷新二级缓存的设置 

<update id="updateMonster" parameterType="Monster" flushCache="true">
	UPDATE mybatis_monster SET NAME=#{name},age=#{age} WHERE id=#{id}
</update>

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

注意

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

EhCache 缓存-细节说明  

 1.    MyBatis 提供了一个接口 Cache  如图,找到 org.apache.ibatis.cache.Cache ,关联源码包就可以看到 Cache 接口

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

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

 

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

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

到这一步了对二级缓存也就没有多少疑惑了。

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

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

相关文章

linux安装nacos步骤

安装前提&#xff1a;服务器已安装JDK 一、nacos下载 Nacos下载地址&#xff1a;Releases alibaba/nacos GitHub 根据springboot版本选择nacos版本 版本说明 alibaba/spring-cloud-alibaba Wiki GitHub 二、nacos解压、修改配置文件 #选择安装目录 cd /home/dxhy/appl…

一款基于 Spring Cloud 开源的医疗信息系统

今天给大家介绍一个医院信息系统开源项目&#xff0c;相对比较完整&#xff0c;采用的技术栈是 Spring cloud和Spring boot 2.x&#xff0c;比较主流&#xff0c;正在做这方面系统的童鞋们可以参考一下&#xff01; 主要功能按照数据流量、流向及处理过程分为临床诊疗、药品管…

云原生|详解Kubernetes Operator在项目中的开发应用

目录 一、使用场景 &#xff08;一&#xff09;client-go中处理逻辑 &#xff08;二&#xff09;controller-runtime中处理逻辑 二、使用controller-runtime开发operator项目 &#xff08;一&#xff09;生成框架代码 &#xff08;二&#xff09;定义crd字段 &#xff0…

分布式消息队列RocketMQ概念详解

目录 1.MQ概述 1.1 RocketMQ简介 1.2 MQ用途 1.3 常见MQ产品 2.RocketMQ 基本概念 2.1 消息 2.2 主题 2.3 标签 2.4 队列 2.5 Producer 2.6 Consumer 2.7 NameServer 2.8 Broker 2.9 RocketMQ 工作流程 1.MQ概述 1.1 RocketMQ简介 RocketMQ 是阿里开源的分布式消…

云原生:从基本概念到实践,解析演进与现状

文章目录 云原生&#xff1a;从基本概念到实践&#xff0c;解析演进与现状概念演进之路DockerKubernetesCloud NativeServerless 业界现状总结 结语 云原生&#xff1a;从基本概念到实践&#xff0c;解析演进与现状 本文仅用于简单普及&#xff0c;达到的目的是给没接触过或者很…

苹果手机无法开机?黑屏打不开怎么办?出现这种问题的解决办法分享!

各位在使用苹果手机的小伙伴有没有遇到苹果手机突然就黑屏开不了机&#xff0c;打电话也没有任何反应&#xff0c;手机也无法关机重启&#xff0c;这是什么问题呢&#xff1f;我们遇到这种问题该如何去处理呢&#xff1f; 小编今天就来跟大家说说苹果手机突然开不了机的原因以及…

【Linux命令】脚本里常用的几个命令

脚本里常用的命令 一、SORT命令1.1、语法格式1.2常用选项 二、uniq命令2.1命令格式2.2常用选项2.3小实验&#xff0c;过滤出现三次以上的IP地址 三、tr命令3.1语法格式3.2常用选项3.3实验 四、cut命令4.1语法格式4.2常用选项 五、split命令5.1语法格式5.2常用选项 六、eval七、…

在行 | “数智”为离散制造发展注入动能

在行业现场解析行业难题&#xff0c; 用主题方案创新数智价值。 制造业作为我国实体经济的主体&#xff0c;是国民经济体系的重要组成部分&#xff0c;其中以离散制造比重最大&#xff0c;是解决就业等民生问题的支柱。随着技术和经济水平的提升&#xff0c;市场对离散制造行业…

CnOpenData淘宝村淘宝镇名单数据

一、数据简介 随着电商的迅猛发展&#xff0c;以淘宝村为代表的新型城镇化不断推进。淘宝镇和淘宝村是电商巨头阿里巴巴推出的一系列支持中小企业、新创企业发展的计划&#xff0c;旨在为中小企业及创新企业提供融资、营销、培训、咨询等服务。截至2022年&#xff0c;全国涌现了…

创新案例 |探索 Tive 80% 的收入增长得益于智能物流服务、跟踪和实时可视化

您正在寻找可靠的物流解决方案吗&#xff1f; Tive 是领先的智能物流服务提供商&#xff0c;提供跟踪和实时可见性解决方案。使用 Tive&#xff0c;您可以主动监控公路、空运、海运和铁路运输。它可以帮助您减少运输问题并确保准时和全面交付&#xff0c;从而改善客户体验。 …

融合CDN和单CDN的产品对比

仅针对特定地理位置的公司可以使用单一CDN解决方案&#xff0c;建议网站内容在全球分发的优先选择融合CDN来进行加速。 如果您的网站内容/应用程序大多是静态的&#xff0c;那么单一CDN解决方案可能适合大多数市场需求&#xff1b;但如果您的流量高于平均水平&#xff0c;媒体流…

【表面缺陷检测】基于yolov5的钢板表面缺陷检测(附代码和数据集,Windows系统)

写在前面: 首先感谢兄弟们的订阅,让我有创作的动力,在创作过程我会尽最大能力,保证作品的质量,如果有问题,可以私信我,让我们携手共进,共创辉煌。 路虽远,行则将至;事虽难,做则必成。只要有愚公移山的志气、滴水穿石的毅力,脚踏实地,埋头苦干,积跬步以至千里,就…

iPhone语音备忘录删除了怎么恢复?恢复备忘录,只需3个方法!

案例&#xff1a;语音备忘录被清空 【苹果语音备忘录有我很多会议记录&#xff0c;但是被我清理手机垃圾的时候顺便清理了。有什么方法恢复回来吗&#xff1f;】 很多人都知道&#xff0c;iphone语音备忘录是使用起来非常方便的一种记录方式&#xff0c;但是如何在不小心删除备…

Python之引用

1. 引用简介与工具引入 Python 中对于变量的处理与 C 语言有着很大的不同&#xff0c;Python 中的变量具有一个特殊的属性&#xff1a;identity&#xff0c;即“身份标识”。这种特殊的属性也在很多地方被称为“引用”。 为了更加清晰地说明引用相关的问题&#xff0c;我们首…

MySQL---多表联合查询(下)(内连接查询、外连接查询、子查询(ALL/ANY/SOME/IN/EXISTS关键字)、自关联查询)

1. 内连接查询 数据准备&#xff1a; use mydb3;-- 创建部门表 create table if not exists dept3(deptno varchar(20) primary key , -- 部门号name varchar(20) -- 部门名字 );-- 创建员工表 create table if not exists emp3(eid varchar(20) primary key , -- 员工编号e…

代表Java未来的ZGC深度剖析

JAVA程序最爽的地方是它的GC机制&#xff0c;开发人员不需要关注内存申请和回收问题。同时&#xff0c;JAVA程序最头疼的地方也是它的GC机制&#xff0c;因为掌握JVM和GC调优是一件非常困难的事情。在ParallelOldGC、CMS、G1之后&#xff0c;JDK11带来的全新的「ZGC」为我们解决…

css中常用伪类表单验证:invalid、:valid、:required、以及:not 、:lang、:empty的使用

MDN文档关于伪类的相关介绍 1、 :invalid :invalid 是 CSS 伪类选择器&#xff0c;用来选择任何未通过验证的 <form>、<fieldset>、<input> 或其他表单元素。 <form class"form"><label for"email">邮箱地址:</label>…

Sqlite3 生成lib库文件

特此记录&#xff01; QT使用SQL一般有两种方式 No1&#xff0c;使用Qt内部的Sql模块 No2&#xff0c;不通过Qt的Sql模块&#xff0c;直接使用Sqlite的lib库&#xff0c;使用Sqlite的标准C/C接口就行 接下来主要针对第二种。 第一步&#xff0c;进入官网 SQLite Download P…

Google Play应用广告该如何运作

Google 应用广告是一种付费广告渠道&#xff0c;可以帮助我们把应用推向特定的目标受众。比如可以使用应用安装广告&#xff0c;用来吸引用户安装我们的应用&#xff0c;我们可以选择手动设置出价和定位&#xff0c;或使用 Google Ads 自动设置目标和出价。 Google 在创建和投…

unity3D 魔兽争霸游戏开发案例教程

文章连载更新中&#xff0c;可以提前领取素材进行预习&#xff0c;自学 素材领取&#xff1a;私信发送 领取RPG网络开发教材 这里写目录标题 游戏玩法这门课适合哪些人学习学完了能达到什么效果项目准备基础系统战斗系统同步设计精讲社交系统副本系统优化项目准备正文美术准备&…