使⽤缓存机制的作⽤也是减少 Java 应⽤程序与数据库的交互次数,从⽽提升程序的运⾏效率。

⽐如第 ⼀次查询出某个对象之后,MyBatis 会⾃动将其存⼊缓存,当下⼀次查询同⼀个对象时,就可以直接从 缓存中获取,不必再次访问数据库。

  MyBatis 有两种缓存:⼀级缓存和⼆级缓存。

MyBatis ⾃带⼀级缓存,并且⽆法关闭,⼀直存在,⼀级缓存的数据存储在 SqlSession 中,它的范围 就是在⼀个SqlSession 对象中。

  当使⽤同⼀个 SqlSession 对象执⾏查询操作时,第⼀次的执⾏结果会 ⾃动存⼊ SqlSession 缓存中,第⼆次查询时直接从缓存中获取即可。

   如果是不同的 SqlSession 对象,则缓存数据⽆法同步。

同时需要注意,为了保证数据的⼀致性,如果 SqlSession 执⾏了增加、删除、修改操作,MyBatis 会 ⾃动清空 SqlSession 缓存中存储的数据。

  ⼀级缓存不需要进⾏任何配置,直接使⽤即可。

  MyBatis ⼆级缓存是⽐⼀级缓存作⽤域更⼤的缓存机制,它是 Mapper 级别,只要是同⼀个 Mapper, ⽆论使⽤多少个SqlSession 来操作,数据都是共享的,多个不同的 SqlSession 可以共⽤⼆级缓存。

  MyBatis ⼆级缓存默认是关闭的,需要使⽤时可以通过配置⼿动开启。

  • 一级缓存,示例:

测试类

package com.sunjian.test;

import com.sunjian.entity.Classes;
import com.sunjian.entity.Order;
import com.sunjian.entity.User;
import com.sunjian.repository.ClassesRepository;
import com.sunjian.repository.OrderRepository;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import sun.reflect.generics.repository.ClassRepository;

import java.io.InputStream;

/**
 * @author sunjian
 * @date 2020/3/22 10:20
 */
public class TestCache {
    public static void main(String[] args) {

        // Mapper 代理实现自定义接口
        InputStream inputStream = User.class.getClassLoader().getResourceAsStream("config.xml");
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 获取接口的处理对象
        ClassesRepository classesRepository = sqlSession.getMapper(ClassesRepository.class);
        Classes classes = classesRepository.findClassById(2);
        System.out.println(classes);

        Classes classes1 = classesRepository.findClassById(2);
        System.out.println(classes1);
    }

}
Opening JDBC Connection
Created connection 793315160.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@2f490758]
==>  Preparing: select c.id cid, c.name cname, s.id sid, s.name sname, s.score, s.tel, s.address from classes c, student s where s.cid = c.id and c.id = ?
==> Parameters: 2(Integer)
<==    Columns: cid, cname, sid, sname, score, tel, address
<==        Row: 2, 2班, 1, 张三, 90, 15510211111, 大望路
<==        Row: 2, 2班, 3, 赵六, 87, 18800110011, 回龙观
<==      Total: 2
Classes{id=2, name='2班', students=[Student{id=1, name='张三', score=90, tel='15510211111', address='大望路', classes=null, course=null}, Student{id=3, name='赵六', score=87, tel='18800110011', address='回龙观', classes=null, course=null}]}
Classes{id=2, name='2班', students=[Student{id=1, name='张三', score=90, tel='15510211111', address='大望路', classes=null, course=null}, Student{id=3, name='赵六', score=87, tel='18800110011', address='回龙观', classes=null, course=null}]}

查询两次,只执行一次SQL。

查询一次后,关闭sqlSession,创建新的sqlSession后,再查询

package com.sunjian.test;

import com.sunjian.entity.Classes;
import com.sunjian.entity.Order;
import com.sunjian.entity.User;
import com.sunjian.repository.ClassesRepository;
import com.sunjian.repository.OrderRepository;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import sun.reflect.generics.repository.ClassRepository;

import java.io.InputStream;

/**
 * @author sunjian
 * @date 2020/3/22 10:20
 */
public class TestCache {
    public static void main(String[] args) {

        // Mapper 代理实现自定义接口
        InputStream inputStream = User.class.getClassLoader().getResourceAsStream("config.xml");
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 获取接口的处理对象
        ClassesRepository classesRepository = sqlSession.getMapper(ClassesRepository.class);
        Classes classes = classesRepository.findClassById(2);
        System.out.println(classes);
        sqlSession.close();

        sqlSession = sqlSessionFactory.openSession();
        // 获取接口的处理对象
        classesRepository = sqlSession.getMapper(ClassesRepository.class);
        Classes classes1 = classesRepository.findClassById(2);
        System.out.println(classes1);
    }

}
Opening JDBC Connection
Created connection 793315160.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@2f490758]
==>  Preparing: select c.id cid, c.name cname, s.id sid, s.name sname, s.score, s.tel, s.address from classes c, student s where s.cid = c.id and c.id = ?
==> Parameters: 2(Integer)
<==    Columns: cid, cname, sid, sname, score, tel, address
<==        Row: 2, 2班, 1, 张三, 90, 15510211111, 大望路
<==        Row: 2, 2班, 3, 赵六, 87, 18800110011, 回龙观
<==      Total: 2
Classes{id=2, name='2班', students=[Student{id=1, name='张三', score=90, tel='15510211111', address='大望路', classes=null, course=null}, Student{id=3, name='赵六', score=87, tel='18800110011', address='回龙观', classes=null, course=null}]}
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@2f490758]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@2f490758]
Returned connection 793315160 to pool.
Opening JDBC Connection
Checked out connection 793315160 from pool.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@2f490758]
==>  Preparing: select c.id cid, c.name cname, s.id sid, s.name sname, s.score, s.tel, s.address from classes c, student s where s.cid = c.id and c.id = ?
==> Parameters: 2(Integer)
<==    Columns: cid, cname, sid, sname, score, tel, address
<==        Row: 2, 2班, 1, 张三, 90, 15510211111, 大望路
<==        Row: 2, 2班, 3, 赵六, 87, 18800110011, 回龙观
<==      Total: 2
Classes{id=2, name='2班', students=[Student{id=1, name='张三', score=90, tel='15510211111', address='大望路', classes=null, course=null}, Student{id=3, name='赵六', score=87, tel='18800110011', address='回龙观', classes=null, course=null}]}

可以看到执行了两次SQL。

  • 二级缓存,示例:

  MyBatis 可以使⽤⾃带的⼆级缓存,也可以使⽤第三⽅的 ehcache ⼆级缓存。

MyBatis ⾃带的⼆级缓存,具体步骤如下。

1、config.xml 中配置开启⼆级缓存

<settings>
    <!-- 开启二级缓存 -->
    <setting name="cacheEnabled" value="true"></setting>
</settings>

2、ClassesRepository.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="com.sunjian.repository.ClassesRepository">

    <cache></cache>

    <!-- 一对多 -->
    <resultMap id="classMap" type="com.sunjian.entity.Classes">
        <id property="id" column="cid"></id>
        <result property="name" column="cname"></result>
        <collection property="students" ofType="com.sunjian.entity.Student">
            <id property="id" column="sid"></id>
            <result property="name" column="sname"></result>
            <result property="score" column="score"></result>
            <result property="tel" column="tel"></result>
            <result property="address" column="address"></result>
        </collection>
    </resultMap>

   <select id="findClassById" parameterType="java.lang.Integer" resultMap="classMap">
       select c.id cid, c.name cname, s.id sid, s.name sname, s.score, s.tel, s.address from classes c, student s where s.cid = c.id and c.id = #{id}
   </select>
</mapper>

3、Classes 实体类实现 Serializable 接⼝。

package com.sunjian.entity;

import java.io.Serializable;
import java.util.List;

/**
 * @author sunjian
 * @date 2020/3/22 14:20
 */
public class Classes implements Serializable {
    private Integer id;
    private String name;
    private List<Student> students;

    @Override
    public String toString() {
        return "Classes{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", students=" + students +
                '}';
    }

    public List<Student> getStudents() {
        return students;
    }

    public void setStudents(List<Student> students) {
        this.students = students;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

4、运行测试类

package com.sunjian.test;

import com.sunjian.entity.Classes;
import com.sunjian.entity.Order;
import com.sunjian.entity.User;
import com.sunjian.repository.ClassesRepository;
import com.sunjian.repository.OrderRepository;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import sun.reflect.generics.repository.ClassRepository;

import java.io.InputStream;

/**
 * @author sunjian
 * @date 2020/3/22 10:20
 */
public class TestCache {
    public static void main(String[] args) {

        // Mapper 代理实现自定义接口
        InputStream inputStream = User.class.getClassLoader().getResourceAsStream("config.xml");
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 获取接口的处理对象
        ClassesRepository classesRepository = sqlSession.getMapper(ClassesRepository.class);
        Classes classes = classesRepository.findClassById(2);
        System.out.println(classes);
        sqlSession.close();

        sqlSession = sqlSessionFactory.openSession();
        // 获取接口的处理对象
        classesRepository = sqlSession.getMapper(ClassesRepository.class);
        Classes classes1 = classesRepository.findClassById(2);
        System.out.println(classes1);
    }

}
Opening JDBC Connection
Created connection 1738236591.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@679b62af]
==>  Preparing: select c.id cid, c.name cname, s.id sid, s.name sname, s.score, s.tel, s.address from classes c, student s where s.cid = c.id and c.id = ?
==> Parameters: 2(Integer)
<==    Columns: cid, cname, sid, sname, score, tel, address
<==        Row: 2, 2班, 1, 张三, 90, 15510211111, 大望路
<==        Row: 2, 2班, 3, 赵六, 87, 18800110011, 回龙观
<==      Total: 2
Classes{id=2, name='2班', students=[Student{id=1, name='张三', score=90, tel='15510211111', address='大望路', classes=null, course=null}, Student{id=3, name='赵六', score=87, tel='18800110011', address='回龙观', classes=null, course=null}]}
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@679b62af]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@679b62af]
Returned connection 1738236591 to pool.
Cache Hit Ratio [com.sunjian.repository.ClassesRepository]: 0.5
Classes{id=2, name='2班', students=[Student{id=1, name='张三', score=90, tel='15510211111', address='大望路', classes=null, course=null}, Student{id=3, name='赵六', score=87, tel='18800110011', address='回龙观', classes=null, course=null}]}

可以看出,即使在第一次查询后,关闭了sqlSession,创建新的sqlSession再次查询时,也是只执行了一次SQL。

  • 使⽤第三⽅的 ehcache ⼆级缓存

1、pom.xml 导⼊ ehcache 相关依赖

<!-- 把ehcache整合到mybatis中 -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-ehcache</artifactId>
    <version>1.0.0</version>
</dependency>

<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache-core</artifactId>
    <version>2.4.3</version>
</dependency>

2、在 resources 路径下创建 ehcache.xml

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
    <diskStore/>
    <defaultCache
            maxElementsInMemory="1000"
            maxElementsOnDisk="10000000"
            eternal="false"
            overflowToDisk="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
    </defaultCache>
</ehcache>

3、config.xml 中配置开启⼆级缓存

<settings>
    <!-- 开启二级缓存 -->
    <setting name="cacheEnabled" value="true"></setting>
</settings>

4、ClassesRepository.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="com.sunjian.repository.ClassesRepository">

    <!-- myBatis自带二级缓存 -->
    <!--<cache></cache>-->

    <!-- 第三方cache二级缓存 -->
    <cache type="org.mybatis.caches.ehcache.EhcacheCache">
        <!-- 缓存创建之后,最后⼀次访问缓存的时间⾄失效的时间间隔 -->
        <property name="timeToIdleSeconds" value="3600"/>
        <!-- 缓存⾃创建时间起⾄失效的时间间隔 -->
        <property name="timeToLiveSeconds" value="3600"/>
        <!-- 缓存回收策略,LRU 移除近期最少使⽤的对象 -->
        <property name="memoryStoreEvictionPolicy" value="LRU"/>
    </cache>

    <!-- 一对多 -->
    <resultMap id="classMap" type="com.sunjian.entity.Classes">
        <id property="id" column="cid"></id>
        <result property="name" column="cname"></result>
        <collection property="students" ofType="com.sunjian.entity.Student">
            <id property="id" column="sid"></id>
            <result property="name" column="sname"></result>
            <result property="score" column="score"></result>
            <result property="tel" column="tel"></result>
            <result property="address" column="address"></result>
        </collection>
    </resultMap>

   <select id="findClassById" parameterType="java.lang.Integer" resultMap="classMap">
       select c.id cid, c.name cname, s.id sid, s.name sname, s.score, s.tel, s.address from classes c, student s where s.cid = c.id and c.id = #{id}
   </select>
</mapper>

5、实体类不需要实现序列化接⼝

package com.sunjian.entity;

import java.util.List;

/**
 * @author sunjian
 * @date 2020/3/22 14:20
 */
public class Classes {
    private Integer id;
    private String name;
    private List<Student> students;

    @Override
    public String toString() {
        return "Classes{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", students=" + students +
                '}';
    }

    public List<Student> getStudents() {
        return students;
    }

    public void setStudents(List<Student> students) {
        this.students = students;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

6、运行测试类

package com.sunjian.test;

import com.sunjian.entity.Classes;
import com.sunjian.entity.Order;
import com.sunjian.entity.User;
import com.sunjian.repository.ClassesRepository;
import com.sunjian.repository.OrderRepository;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import sun.reflect.generics.repository.ClassRepository;

import java.io.InputStream;

/**
 * @author sunjian
 * @date 2020/3/22 10:20
 */
public class TestCache {
    public static void main(String[] args) {

        // Mapper 代理实现自定义接口
        InputStream inputStream = User.class.getClassLoader().getResourceAsStream("config.xml");
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 获取接口的处理对象
        ClassesRepository classesRepository = sqlSession.getMapper(ClassesRepository.class);
        Classes classes = classesRepository.findClassById(2);
        System.out.println(classes);
        sqlSession.close();

        sqlSession = sqlSessionFactory.openSession();
        // 获取接口的处理对象
        classesRepository = sqlSession.getMapper(ClassesRepository.class);
        Classes classes1 = classesRepository.findClassById(2);
        System.out.println(classes1);
    }

}
Opening JDBC Connection
Created connection 952486988.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@38c5cc4c]
==>  Preparing: select c.id cid, c.name cname, s.id sid, s.name sname, s.score, s.tel, s.address from classes c, student s where s.cid = c.id and c.id = ?
==> Parameters: 2(Integer)
<==    Columns: cid, cname, sid, sname, score, tel, address
<==        Row: 2, 2班, 1, 张三, 90, 15510211111, 大望路
<==        Row: 2, 2班, 3, 赵六, 87, 18800110011, 回龙观
<==      Total: 2
Classes{id=2, name='2班', students=[Student{id=1, name='张三', score=90, tel='15510211111', address='大望路', classes=null, course=null}, Student{id=3, name='赵六', score=87, tel='18800110011', address='回龙观', classes=null, course=null}]}
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@38c5cc4c]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@38c5cc4c]
Returned connection 952486988 to pool.
Cache Hit Ratio [com.sunjian.repository.ClassesRepository]: 0.5
Classes{id=2, name='2班', students=[Student{id=1, name='张三', score=90, tel='15510211111', address='大望路', classes=null, course=null}, Student{id=3, name='赵六', score=87, tel='18800110011', address='回龙观', classes=null, course=null}]}

同样可以看出,即使在第一次查询后,关闭了sqlSession,创建新的sqlSession再次查询时,也是只执行了一次SQL。

OK.

MyBatis框架——缓存机制的更多相关文章

  1. mybatis的缓存机制及用例介绍

    在实际的项目开发中,通常对数据库的查询性能要求很高,而mybatis提供了查询缓存来缓存数据,从而达到提高查询性能的要求. mybatis的查询缓存分为一级缓存和二级缓存,一级缓存是SqlSessio ...

  2. mybatis(四)缓存机制

    转载:https://www.cnblogs.com/wuzhenzhao/p/11103043.html 缓存是一般的ORM 框架都会提供的功能,目的就是提升查询的效率和减少数据库的压力.跟Hibe ...

  3. 深入浅出mybatis之缓存机制

    目录 前言 准备工作 MyBatis默认缓存设置 缓存实现原理分析 参数localCacheScope控制的缓存策略 参数cacheEnabled控制的缓存策略 总结 前言 提到缓存,我们都会不约而同 ...

  4. MyBatis - 5.缓存机制

    MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制.缓存可以极大的提升查询效率. MyBatis系统中默认定义了两级缓存. 一级缓存和二级缓存. 1.默认情况下,只有一级缓存( ...

  5. MyBatis 的缓存机制

    缓存机制可以减轻数据库的压力,原理是在第一查询时,将查询结果缓存起来,之后再查询同样的sql, 不是真的去查询数据库,而是直接返回缓存中的结果. 缓存可以降低数据库的压力,但同时可能无法得到最新的结果 ...

  6. mybatis的缓存机制(一级缓存二级缓存和刷新缓存)和mybatis整合ehcache

    1.1  什么是查询缓存 mybatis提供查询缓存,用于减轻数据压力,提高数据库性能. mybaits提供一级缓存,和二级缓存. 一级缓存是SqlSession级别的缓存.在操作数据库时需要构造 s ...

  7. mybatis的缓存机制

    一级缓存: MyBatis的一级缓存指的是在一个Session域内,session为关闭的时候执行的查询会根据SQL为key被缓存(跟mysql缓存一样,修改任何参数的值都会导致缓存失效) packa ...

  8. 聊聊MyBatis缓存机制【美团-推荐】

    聊聊MyBatis缓存机制 2018年01月19日 作者: 凯伦 文章链接 18778字 38分钟阅读 前言 MyBatis是常见的Java数据库访问层框架.在日常工作中,开发人员多数情况下是使用My ...

  9. 聊聊MyBatis缓存机制

    https://tech.meituan.com/mybatis_cache.html 前言 MyBatis是常见的Java数据库访问层框架.在日常工作中,开发人员多数情况下是使用MyBatis的默认 ...

随机推荐

  1. 吴裕雄--天生自然 R语言开发学习:基本图形(续二)

    #---------------------------------------------------------------# # R in Action (2nd ed): Chapter 6 ...

  2. JAVA 获取网页源代码保存到本地文件

    package httpget; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundExce ...

  3. Ionic3学习笔记(十)实现夜间模式功能

    本文为原创文章,转载请标明出处 目录 创建主题样式 导入 variables.scss 创建 provider 创建 page 在 App 入口处应用主题 效果图 1. 创建主题样式 在 ./src/ ...

  4. python爬取许多图片的代码

    from bs4 import BeautifulSoup import requests import os os.makedirs('./img/', exist_ok=True) URL = & ...

  5. 「NOIP 2017」列队

    题目大意:给定一个 $n times m$ 的方阵,初始时第 $i$ 行第 $j$ 列的人的编号为 $(i-1) times m + j$,$q$ 次给出 $x,y$,让第 $x$ 行 $y$ 列的人 ...

  6. Scheme实现数字电路仿真(3)——模块

    版权申明:本文为博主窗户(Colin Cai)原创,欢迎转帖.如要转贴,必须注明原文网址 http://www.cnblogs.com/Colin-Cai/p/12242650.html 作者:窗户 ...

  7. 解决Request中参数中文乱码问题

    1.使用配置过滤器的方式解决 在web.xml中增加过滤器: <!--配置解决中文乱码的过滤器--> <filter> <filter-name>character ...

  8. iOS开发线程同步技术-锁

    概览 1,什么是锁(临界区)? 2,常用的锁有哪些? 3,相关链接 什么是锁(临界区) 临界区:指的是一块对公共资源进行访问的代码,并非一种机制或是算法. 常用的锁有哪些? 互斥锁:是一种用于多线程编 ...

  9. Webpack 核心开发者 Sean Larkin 盛赞 Vue

    dev.io 近日邀请了 Webpack 核心开发者 Sean Larkin 回答开发者提问,其中几个问提比较有意思,和掘金的小伙伴们分享一下. 先上点前菜: 有一个开发者问 Sean 如何成为一个热 ...

  10. 大厂常问iOS面试题--性能优化篇

    1.造成tableView卡顿的原因有哪些? 1.最常用的就是cell的重用, 注册重用标识符 如果不重用cell时,每当一个cell显示到屏幕上时,就会重新创建一个新的cell 如果有很多数据的时候 ...