从JBDC到常用的ORM框架设计一级、二级缓存优化

目录

JDBC

orm框架的出现

1. 什么是ORM?

2.为什么使用ORM?

mybatis框架缓存使用

MyBatis 与 Hibernate

MyBatis中configuration 配置

查询缓存


JDBC

JDBC英文全称为Java DataBase Connectivity,即Java数据库连接,也就是Java程序连接数据库的一种技术。说白了,就是当程序代码和数据库需要进行数据传输(交互)的时候使用的一门技术。

JDBC技术的基础使用

从上面的描述我们知道,除了搭建开发环境之外,搭建开发环境也就是需要使用哪些软件,因为本文是阐述JDBC的,这里就不作过多介绍咯,不会搭建java开发环境的可以问度娘,比较简单。还需要的就是把之前说的接口和实现准备好,因为接口是在jdk中,无需准备,实现的话,我们本次使用的是MySQL数据库,所以需要导入MySQL数据库的jar包,在百度中可以随意下载一个,比如我下载了一个5.1的版本,名称为"mysql-connector-java-5.1.17-bin.jar",然后导入到MyEclipse开发环境中。这里先作一个说明,数据库dbjdbc和数据表users我都已经建好了,Java项目JDBCDemo也建好了,就不作过程的阐述咯。

 

(1)加载MySQL的驱动类(要想使用mysql的实现类,得先注册一把)

Class.forName("com.mysql.jdbc.Driver");

为啥要这么做,加MySQL的驱动类加载到JVM中,说白了就是咱们要使用MySQL数据库的jar包了,是不是先得注册一把?

 

(2)根据DriverManager得到Connection对象(要java程序和数据库发生交互,得先连接上对应的数据)

Connection conn=DriverManager.getConnection(url, user, password);

a.其中url="jdbc:mysql://localhost:3306/dbjdbc",user="root",password="root"

b.url表示要连接到mysql的哪个数据库,mysql的默认端口是3306,前面的无需改,dbjdbc这里填写你自己的数据库名称

c.user填写mysql数据库的用户名

d.password填写mysql数据库的密码

e.之前我们说过,要使用JDBC技术,无非就是找类,找方法,找属性等,那么java代码要想和数据库发生联系,必定得先要有一个对象连接上对应的数据库,这个对象就是Connection对象,从名称中也可以看出,但是要注意的是Connection conn定义导包的时候,最好选择java.sql中的Connection,以接口的形式声明,这样以后的扩展性比较好,这就是多态的应用,这里不展开论述,有机会专门说说多态。

 

(3)根据连接对象Connection得到Statement对象(Connection对象只负责连接到哪个数据库,要想进行增删改查操作得重新找一个对象,比如Statement对象)

Statement stmt = conn.createStatement();

有了Connection连接对象之后,我们就知道具体要连接哪个数据库,接下来就是要进行具体的增删改查操作了,那怎么做呢?记住万物皆对象,Connection对象是负责连接的,而负责进行增删改查的对象又是什么呢?没错,就是Statement对象,当然这里是以Statement对象为例,还可以是其他对象,毕竟能做成一件事的途径不止一个,我们这里是为了演示使用。而Statement对象操作的是哪个数据库,这取决于由哪个Connection对象生成的Statement对象,这点很好理解吧?不过要记住,Statement对象在导包的时候也需要导入java.sql中的,原理和上面一样。

 

(4)增加(准备需要增加的sql语句,交给Statement对象的executeUpdate(sql)执行)

好了,说了这么多,接下来总可以进行插入操作了吧?也就是在java代码中通过jdbc技术向数据表中插入数据。这里肯定要准备一条sql语句,因为sql语句知道向哪个表中插入什么样的数据对不对?比如写上一条sql语句,String sql="insert into users(name) values('插入一条新数据!')"。那么如何让这条sql语句生效呢?这时候就想到了Statement对象,只要调用该对象的executeUpdate方法即可,并且将sql语句作为参数传入。返回值为int类型,表示受影响的行数,倘若上述的sql语句执行成功了,就会有一行受影响。

执行结果

 

 

(5)修改(修改的原理和增加的原理一样,准备一条需要修改的sql语句)

比如我们需要将id为1的name值改成"欢迎关注忆说就懂微信公众号",准备一条sql语句

String sql="update users set name='欢迎关注忆说就懂微信公众号' where id=1";

然后将该sql语句同样交给Statement对象的executeUpdate方法即可,如下图所示

执行结果

 

(6)删除(删除的原理和增加的原理一样,准备一条需要删除的sql语句)

比如我们需要将id为3的记录删除,准备一条sql语句

String sql="delete from users where id=3";

然后将该sql语句同样交给Statement对象的executeUpdate方法即可,如下图所示

执行结果

 

 

(7)查询

通过上面的增删改案例可以看出,增删改的原理是一样的,都是准备一条需要的sql语句,然后交给Statement对象的executeUpdate方法执行即可。那么查询呢?毋庸置疑,查询肯定也需要准备一条sql语句,因为你得告诉程序需要查询哪张表,哪条记录,查询条件是什么等等。这里我们查询users表的所有记录,然后打印输出到控制台。

String sql="select * from users";

然后将该sql语句交给Statement对象,此时调用的是executeQuery,从名称中可以看出,Query的意思是查询。一般使用一个方法,我们往往关心的是参数列表和返回值,这里的参数是sql,那么返回值是什么呢?既然是查询,那么返回值肯定是查询的结果,这个结果使用的是一个对象保存,即ResultSet,因为万物皆对象,该对象的结构和数据表的结构是一样的,要想取出其中的数据,循环读取出来,下面我们直接看代码。

从while循环开始理解,while(rs.next())表示循环读取行,数据表中每有一行就循环一次,这个比较容易理解,比如目前数据表中有两条记录,那么该循环就执行两次。

rs.getXxx明显是取出每行的值,比如第一次循环,读取的是第一行,我们知道对于表格,只要知道行与列就可以对应到某个具体单元格的值,那么现在行有了,列呢?rs.getXxx括号中的参数可以是列的序号,比如1,2,3表示第一列,第二列,第三列,也可以是列的名称,比如"id","name"。但是要注意,如果使用标号,该标号是从1开始的,因为程序中通常是从下标0开始,这点不同,另外如果通过列名来获取值,列名要和数据表中的名称对应,不然会报错,取不到值。至于rs.getXxx有时候是Int,有时候是String,是根据数据表中该列的类型决定的。这样一说,是不是非常容易理解。

比如在while进行第一次循环时,rs.getInt("id")表示取出第一行中列名为"id"的单元格的值,也就是第一行第一列的值,发现是"1";比如在while进行第二次循环时,rs.getString(2)表示取出第二行中第二列的单元格的值,发现是"JDBC",我们来看下运行的结果。

5.JDBC技术的进一步使用

有了上面的增删改查基础之后,我们不妨来看一个具体的案例,以"管理员登录"为例,查找admin表中是否存在该用户名和密码,从而判断是否登录成功。主要是为了了解JDBC的进一步使用。好了,咱们来开始。

首先来创建一张"admin"的数据表,如下图所示,其中有这些字段

然后我们使用之前的jdbc增加的技术对给admin表中插入一条记录,代码如下图所示

这样就可以向admin数据表中插入一条对应的记录,这里我就不截图数据表的图片了,肯定可以插入成功。

 

然后咱们定义一个方法叫做login,其中有两个参数username,password,模拟用户登录的情况。我们的想法是,当别人传来username和password的时候,我们用这两者作为select语句的查询条件部分,如果这样从数据表中能够查询到对应的记录,说明用户名和密码正确,否则用户名或者密码错误。sql语句如下图所示,这里进行了字符串的拼接,因为外面传来的是两个变量,直接写成"select * from admin where username=username and password=password"显然不合适,这样查询条件就变成了用户名为username,密码为password了,所以需要进行字符串的拼接。

将该sql语句交给Statement对象的executeQuery方法执行,得到一个ResultSet对象,最后我们来判断ResultSet对象中是否有数据就可以得出用户名和密码是否正确,流程比较简单,但这里想说明几个问题。

 

(1)拼接字符串好麻烦,能不能不拼接字符串?

大家肯定会有这样一个疑惑,万一后面的条件变多了,字段变多了,那么拼起来更加麻烦,还容易出错,那有没有解决方案呢?答案是肯定的,需要引出另外一个对象PreparedStatement,这个对象也是用于增删改查操作的,只不过和Statement对象有点不同,具体哪边不同,我们来看下面的改造。

 

a.得到PreparedStatement对象

之前通过Connection对象的createStatement方法可以得到Statement对象,那么PrepareStatement对象如何获得呢?如下图所示,只要调用Connection对象的prepareStatement方法即可,与createStatement对象不同的是,它需要接受一个sql语句,所以我们需要将sql语句放到创建该对象之前,这倒没问题太大问题,关键是这样有什么好处吗?再来看第二点

 

b.有了PreparedStatement对象之后,sql语句就可以进行改写

String sql="select * from admin where username=? and password=?";

有没有发现方便很多?那其中的"?"表示什么,它代表的是占位符,也就是它先把位置占在这,具体值是什么,接下来再指定

 

c.指定占位符的值

发现通过PreparedStatement对象可以对占位符的值进行指定,有了前面getXxx的基础,这里用pst.setXxx就很容易理解了吧?因为数据表中的username和password字段是varchar类型,所以这里用String来设置,第一个参数1和2,表示sql语句中占位符的顺序,比如1代表username=?中的"?",第二个参数表示用什么值来代替此占位符,发现是用别人传来的参数username和password进行代替的,这样是不是就简化了sql语句的写法,不用那样复杂的拼接字符串了。

 

d.执行查询操作

那接下来怎样进行查询操作呢?如下图所示,也是调用PreparedStatement对象的excuteQuery方法,与Statement对象不同的是,此时不用传sql语句了,因为在Connection对象创建PreparedStatement对象时已经把sql语句传入进行了预处理,得到的结果仍然是ResultSet对象,这点不难理解,同样是一个表格的形式。

 

(2)调用一下login方法,来做一个测试

好了,PreparedStatement对象咱们也会使用了,那接下来就模拟一下登录,在主函数中调用该login方法,传入username和password。为了看到效果,我们将ResultSet的结果进行一下判断,然后打印在控制台。这里之所以用if,是因为数据表中只有一条记录,如果能查询到ResultSet中也是一条记录,进行一次读取即可。

然后在主函数中进行调用,登录成功代码

运行结果

登录失败代码

运行结果

 

(3)好了,到了这里,应该说JDBC的基本操作已经都OK,还有一个问题需要说明一下

在根据username和password进行查询的时候,得到一个ResultSet结果集,我们肯定不能单单打印出里面的值,需要用一个对象将用户登录成功的数据保存起来,因为万物皆对象,而且在登录成功之后,比如需要显示用户的信息,像Xxx欢迎你等等,肯定不能说再查一次数据表,这样没有意义,也就是说当用户登录成功之后,我们需要用对象将其保存。

 

所以咱们需要设计一个对象,也就是实体类,用于保存用户登录成功之后的数据,如下图所示,于是我们设计了这样一个实体类,实体类的类名和数据表的表明对应,实体类的属性名称和数据表的字段名称对应,这是一般的设计原则,大家对为什么需要实体类有疑惑,可以关注下我们的微信公众号"忆说就懂",我们会写一份关于实体类的文章。总之现在知道一点,当用户登录成功之后,数据在ResultSet中,我们需要将ResultSet中的数据搬运到该实体类中,具体怎么做呢?

如下图所示,创建一个Admin对象,用于保存接下来的数据,然后读取ResultSet中的数据,读到每一行每一列的值分别赋值给admin对象里面的属性,通过set方法。另外,需要将login方法的返回值从void改成Admin,因为调用login方法后,需要得到一个实体类对象,这样别人才可以使用到该实体类对象中保存的数据,这点很容易理解吧?但是要注意,admin=new Admin()这句话一定要放到if语句中,不然通过判断admin是否为null的时候每次都是登录成功,细心的你一定知道是为什么。

于是我们在主函数中调用的时候,就可以根据该Admin对象是否为null,从而判断用户名和密码是否正确了,下面只演示了一下登录成功的情况,登录失败就不作截图咯。从代码和运行结果可以看出,不仅可以判断出用户名和密码是否正确,而且可以通过Admin对象拿到登录成功者的相关信息。

运行结果为

(4)但是不知道大家有没有发现,实际上这样的jdbc代码写起来还是有点麻烦的,一方面sql语句那里需要占位符,然后又要给占位符设置值,另外一方面在将ResultSet中的数据搬运到实体类中时,过程也麻烦。如果一个数据表中的字段很多,这样的过程写起来是非常痛苦的,而且感觉没什么价值。所以对于jdbc代码纵然可以实现crud操作,但不是很方便,于是有了各种对jdbc代码封装的框架,比如DBUtils,Hibernate,MyBatis等等,也就是它们把那些麻烦的操作都给封装起来了,只要我们学会使用它们即可,当然,框架的优势不仅如此,还有很多其他的优势,同时也有弊端,这里就不展开说明咯,等聊到具体的框架时咱们再分析。

 

orm框架的出现

java是一门编程语言,控制程序逻辑
产生的数据,需要用另外程序来处理和保存(数据库、SQL专门用来操作数据的)

通信:标准 :java出了一套自己的标准 JDBC(Java DataBase Connection)  java和数据库连接的标准
跟数据库通信的规则:数据库桥接协议 jdbc: 

第一步:要建立连接
第二步:要登陆数据库 用户名和密码
第三步:要通过Java程序来发送SQL命令给数据库,执行Java发过来的这些SQL(SQL语句,字符串)
第四步:要通过Java程序能够接收到数据所返回的结果。(包装)

 

ORM框架:
MyBatis  Hibernate  SpringJDBC  JPA

1. 什么是ORM?

对象-关系映射(Object-Relational Mapping,简称ORM),面向对象的开发方法是当今企业级应用开发环境中的主流开发方法,关系数据库是企业级应用环境中永久存放数据的主流数据存储系统。对象和关系数据是业务实体的两种表现形式,业务实体在内存中表现为对象,在数据库中表现为关系数据。内存中的对象之间存在关联和继承关系,而在数据库中,关系数据无法直接表达多对多关联和继承关系。因此,对象-关系映射(ORM)系统一般以中间件的形式存在,主要实现程序对象到关系数据库数据的映射。

2.为什么使用ORM?

当我们实现一个应用程序时(不使用O/R Mapping),我们可能会写特别多数据访问层的代码,从数据库保存、删除、读取对象信息,而这些代码都是重复的。而使用ORM则会大大减少重复性代码。对象关系映射(Object Relational Mapping,简称ORM),主要实现程序对象到关系数据库数据的映射。

 

mybatis框架缓存使用

MyBatis 与 Hibernate

Hibernate提供全面的数据封装机制,是**全自动的**ORM,实现POJO和数据库表之间的映射,以及SQL自动生成和执行

MyBatis是**半自动的**ORM框架,不会自动生成SQL语句,需要自己编写,通过SQL语句映射文件将SQL所需的参数以及返回结果字段映射到指定POJO

MyBatis特点

  1. 在xml中配置SQL语句实现SQL语句与代码分离,给维护带来便利
  2. 可以结合数据库自身特点灵活控制SQL语句,具有更高的查询效率

MyBatis中configuration 配置


<?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>

    <settings>
        <!--当返回行的所有列都是空时,MyBatis默认返回null-->
        <setting name="jdbcTypeForNull" value="NULL"/>

        <!--cacheEnabled - 使全局的映射器启用或禁用缓存-->
        <setting name="cacheEnabled" value="true"/>

    </settings>

  <!--类别名配置,配置后可以通过简单类名代替全限定类名-->
    <typeAliases>
        <typeAlias type="beans.UserInfo" alias="UserInfo"></typeAlias>
    </typeAliases>

    <!-- 配置运行环境-->
    <environments default="mysql">
        <environment id="mysql">
            <!-- MyBatis 中有两种事务管理器类型,分别是:-->
            <!--1. type = "JDBC" (依赖于数据源得到的连接来管理事务范围)-->
            <!--2. type = "MANAGED" (不提交或回滚连接,让容器来管理事务的整个周期)-->
            <transactionManager type="JDBC"/>

            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/conference"/>
                <property name="username" value="root"/>
                <property name="password" value="qwer123456"/>
                <!--poolMaximumActiveConnections – 正在使用连接的数量。默认值:10 -->
                <!--poolMaximumIdleConnections – 任意时间存在的空闲连接数-->
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="userinfoMapper.xml"/>
    </mappers>

</configuration>

 

#{ }中可以放的内容:

  • 参数对象的属性
  • 随意内容,此时的#{ }相当于一个占位符
  • 接口方法参数类型Map时,#{}里可以存放Map对象的key
  • 接口方法参数类型Map且Map对象的value为实体类对象时,#{}里可以存放该实体类对象对应的属性
  • 参数的索引号

延迟加载

指在进行关联查询时,按照设置延迟规则推迟对关联对象的select查询,以减小数据库的压力。MyBatis只对关联对象的查询有延迟设置,对主加载对象是直接执行查询语句。

MyBatis根据关联对象查询的select语句的执行时机分为3种类型:

  • 直接加载(执行完主加载对象的select语句,马上执行关联对象的select查询)
  • 侵入式延迟加载(执行对主加载对象的查询时,不会执行对关联对象的查询,当要访问主加载对象详情时才会执行关联的select查询)
  • 深度延迟加载(执行对主加载对象的查询时,不会执行对关联对象的查询,访问主加载对象的详情时也不会执行关联对象的select查询,只有当真正访问关联对象详情时才会执行对关联对象的select查询)

延迟加载应用要求:

关联对象的查询与主加载对象的查询是分别进行的select语句,不能使用多表连接所进行的select查询

延迟加载设置: 在MyBatis主配置文件里的settings标签下添加namelazyLoadingEnabledvaluetrue 的setting标签

<settings>
        <!--当返回行的所有列都是空时,MyBatis默认返回null-->
        <setting name="jdbcTypeForNull" value="NULL"/>

        <!--cacheEnabled - 使全局的映射器启用或禁用缓存-->
        <setting name="cacheEnabled" value="true"/>
        <!--lazyLoadingEnabled - 全局的延迟加载设置-->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!--aggressiveLazyLoading - 侵入式延迟加载设置-->
        <setting name="aggressiveLazyLoading" value="flase"/>

</settings>


查询缓存

MyBatis查询缓存的作用域是根据映射配置文件mapper.xmlnamespace划分的,相同的namespace的mapper查询数据存放在同一个缓存区域,不同的namespace下的数据互不干扰。

无论是一级缓存还是二级缓存都是按照namespace进行分别存放的

MyBatis查询缓存机制根据缓存区的作用域(生命周期)可分为两种

  • 一级查询缓存 
    • 一级查询缓存是基于org.apache.ibatis.cache.impl.PerpetualCache类的HashMap本地缓存,其生命周期为整个SqlSession,同一个SqlSession执行两次相同的sql查询,第一次执行完毕后会将结果写入缓存,第二次查询时直接从缓存里查询。MyBatis一级查询缓存默认开启且不能关闭
    • MyBatis中一级查询缓存读取数据的依据是:**mapper配置文件 查询标签select的**id属性和sql语句;Hibernate中查询缓存的依据是:查询结果对象的id
  • 二级查询缓存 
    • 使用二级缓存的目的不是为了在多个查询间共享查询结果(Hibernate就是为了共享查询结果而设计的),而是为了防止同一个查询的返回执行
    • 二级缓存清空的实质是对所查找的key对应的value置为null,并不是删除Map对象中存放的Entry
    • 使用了缓存要到数据库中查找的情况有两种: 
      1. 缓存Map对象中没有要查找的 key
      2. 缓存中 key 值存在,但value为 null

开启内置二级缓存的步骤:

  • 对实体进行序列化,被缓存的Bean要求实现 java.io.Serializable接口
  • 在映射文件中添加<cache/>标签

二级缓存使用Demo

// 接口定义
public interface IUsrImageDao {
    Set<UsrImage> queryImageInfoByUserId(Integer uid);
}

UsrImage定义如下:

这里写图片描述

// 测试类
@Test
    public void queryImageInfoByUserIdTest() throws IOException {
        InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
        // 创建第一个SqlSession并创建dao实例
        SqlSession session = factory.openSession(true);
        IUsrImageDao dao = session.getMapper(IUsrImageDao.class);
        System.out.println("二级缓存测试\n第一次查询");
        Set<UsrImage> images = dao.queryImageInfoByUserId(4);
        for (UsrImage element : images) {
            System.out.println(element.toString());
        }

        // 执行完成后关闭SqlSession        
        session.close();

        System.out.println("第二次查询");
        // 再次创建SqlSession对象并获取dao实例
        session = factory.openSession(true);
        dao = session.getMapper(IUsrImageDao.class);
        // 再次执行一样的查询
        images = dao.queryImageInfoByUserId(4);
        for (UsrImage element : images) {
            System.out.println(element.toString());
        }

    }

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">
<mapper namespace="dao.IUsrImageDao">

    <cache eviction="LRU" size="512"/>
    <!--<cache/>-->
    <select id="queryImageInfoByUserId" resultMap="imageMapper" useCache="true">
        select name,url,image.id,email,username
        from image,account
        where user_id = #{id} and user_id = account.id
    </select>

    <resultMap id="imageMapper" type="UsrImage">
        <id column="id" property="image_id"/>
        <result column="name" property="image_name"/>
        <result column="url" property="image_url"/>
        <result column="date" property="upload_date"/>
        <association property="user" javaType="UserInfo">
            <id column="id" property="id"/>
            <result column="username" property="username"/>
            <result column="pwd" property="pwd"/>
            <result column="email" property="email"/>
        </association>
    </resultMap>
</mapper>
 

运行结果 
这里写图片描述

二级缓存的使用原则

  • 多个namespace不要操作同一张表
  • 不要在关联关系表上增删改操作
  • 查询多于修改时使用二级缓存

第三方缓存 —— ehcache 使用步骤

  • 添加ehcache 依赖的jar包或在Maven添加依赖

    <!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
      <dependency>
        <groupId>org.mybatis.caches</groupId>
        <artifactId>mybatis-ehcache</artifactId>
        <version>1.1.0</version>
    </dependency>

      <!-- https://mvnrepository.com/artifact/net.sf.ehcache/ehcache-core -->
      <dependency>
        <groupId>net.sf.ehcache</groupId>
        <artifactId>ehcache-core</artifactId>
        <version>2.6.6</version>
    </dependency>

  • 在mapper配置文件里的<cache/> 标签里的 type 属性指明ehcache的类型

       <cache type = "org.mybatis.caches.ehcache.EhcacheCache" />
 

  • classpath下添加配置文件,名称为ehcache.xml

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<diskStore path="java.io.tmpdir"/>
<defaultCache
          maxElementsInMemory="10000"
          eternal="false"
          timeToIdleSeconds="120"
          timeToLiveSeconds="120"
          maxElementsOnDisk="10000000"
          diskExpiryThreadIntervalSeconds="120"
          memoryStoreEvictionPolicy="LRU">
      <persistence strategy="localTempSwap"/>
  </defaultCache>
</ehcache>     

<cache />语句的效果:

  • 映射文件中所有的select语句将被缓存
  • 执行映射文件中任意insertupdatedelete语句缓存将被刷新
  • 缓存采用LRU(最近最久未使用)算法回收
  • 缓存不会被设定的时间所清空
  • MyBatis的缓存是可读/可写的缓存,意味着对象检索不是共享的,而是可以安全地被调用者修改,而不会干扰其它线程所做的潜在修改

缓存的回收策略有:

  • LRU
  • FIFO
  • SOFT -软引用(基于垃圾回收器状态和软引用规则的对象)
  • WEAK-弱引用(强制性地移除基于垃圾收集器状态和弱引用规则的对象)

<!--  创建1个FIFO缓存,每60s清空缓存,存储512个对象或列表引用,返回结果只读 -->
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>

一级缓存和二级缓存的不同点

  • 一级缓存的生命周期为SqlSession,一旦关闭SqlSession缓存就无效
  • 二级缓存是与整个应用同步,一级缓存是同一个线程共享,二级缓存是多个线程共享

查询缓存的底层实现是Map,value里保存查询结果,key为查询依据;MyBatis中增删改操作(无论有没有提交)都会刷新一级缓存,把缓存清空。

借鉴网站:https://blog.csdn.net/itcrazy2016/article/details/73612444

                    https://blog.csdn.net/u011867674/article/details/78941848

                    https://blog.csdn.net/jianyuerensheng/article/details/50804360

                    https://blog.csdn.net/qq_34332010/article/details/77369880

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值