MyBatis

MyBatis

  1. 回顾
    1. Java的应用开发,servlet和Jsp。
    2. MVC中的model,数据访问使用的是JDBC。
      • 取到数据库中的数据,将数据封装到对象中,手动操作。
        1. 存在的问题:
          1. 手动输入的时候,会写错
          2. 代码重复量高
  2. 企业级框架
    1. 从今天开始学习
    2. 框架相当于模版,将主要的技术功能进行了封装,帮助开发人员更专注于你的业务内容,提高企业项目的开发效率。
  3. 什么是 MyBatis ?
    1. MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
  4. 从 XML 中构建 SqlSessionFactory
    1. 每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为中心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先定制的 Configuration 的实例构建出 SqlSessionFactory 的实例。
  5. 数据库连接信息
    1. XML 配置文件(configuration XML)中包含了对 MyBatis 系统的核心设置,包含获取数据库连接实例的数据源(DataSource)和决定事务作用域和控制方式的事务管理器(TransactionManager)。
    2. 配置一个数据配置的属性
    3. dbconfig.properties
      driver=com.mysql.jdbc.Driver
      url=jdbc:mysql://localhost:3306/oa
      username=root
      password=root

       

  6. XML中的配置
    1. <?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>
      	<properties resource="dbconfig.properties"></properties>
      	<environments default="development">
      		<environment id="development">
      			<transactionManager type="JDBC" />
      			<dataSource type="POOLED">
      				<property name="driver" value="${driver}" />
      				<property name="url" value="${url}" />
      				<property name="username" value="${username}" />
      				<property name="password" value="${password}" />
      			</dataSource>
      		</environment>
      	</environments>
      	<mappers>
      		<mapper resource="com/compnay/bean/EmployeeMapper.xml" />
      	</mappers>
      </configuration>

       

  7. MyBatis 提供的全部特性可以利用基于 XML 的映射语言来实现
    1. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
      <mapper namespace="com.company.dao.EmployeeDao">
      	<select id="selectOne" resultType="com.company.bean.Employee">
      		select * from Employee where eid = #{id}
      	</select>
      </mapper>

       

  8. Java代码演示
    1. //获得当前Mybatis总的配置文件路径
      String resource = "configuration.xml";
      //获得当前配置文件的输入流
      InputStream inputStream = Resources.getResourceAsStream(resource);
      //通过流对象来创建一个了SessionFactory对象,数据库会话工厂
      SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
      //通过数据库会话工厂,开启数据库的一次会话
      SqlSession sqlSession = sqlSessionFactory.openSession(true);
      //sqlSession使用sql,然后关闭
      //所有的数据库操作,都被分装到了Session当中了,增、删、改、查
      Employee obj = sqlSession.selectOne("com.company.dao.EmployeeDao.selectOne",10001);
      System.out.println(obj.getEid());
      System.out.println(obj.geteName());
      System.out.println(obj.geteAddr());
      System.out.println(obj.geteSex());

       

  9. 从 SqlSessionFactory 中获取 SqlSession
  10. 既然有了 SqlSessionFactory ,顾名思义,我们就可以从中获得 SqlSession 的实例了。SqlSession 完全包含了面向数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。例如
    1. SqlSession sqlSession = sqlSessionFactory.openSession();
      Employee obj = sqlSession.selectOne("com.company.dao.EmployeeDao.selectOne",10001);

       

  11. 一种更直白的方式:
    1. 创建数据访问的接口
    2. SqlSession sqlSession = sqlSessionFactory.openSession();
      EmployeeDao employeeDao = sqlSession.getMapper(EmployeeDao.class);
      Employee employee = employeeDao.selectOne(10002);

       

  12. 升级项目,通过sqlSession的getMapper方法得到接口的对象
    1. 创建接口,创建接口中的方法:
      1. public interface EmployeeDao {
        	public List<Employee> selectAll();
        }

         

    2. 在Mapper.xml配置文件中,增加查询所有员工的方法
      1. <select id="selectAll" resultType="com.company.bean.Employee">
        	select * from employee
        </select>

         

    3. 测试类中调用
      1. EmployeeDao employeeDao = sqlSession.getMapper(EmployeeDao.class);
        //这个接口调用的方法名称必须和xml中的id指定的名称相同 
        List<Employee> employees = employeeDao.selectAll();

         

  13. 创建接口中的抽象方法
    1. public interface EmployeeDao {
      	public List<Employee> selectAll();
      	public Employee selectOne(int id);
      	public void addEmp(Employee emp);
      	public void updateEmp(Employee emp);
      	public void deleteEmp(int id);
      	public List<Employee> queryList(String name,char sex);
      	public List<Employee> queryList2(@Param("name") String name,@Param("sex") char sex);
      	public List<Employee> queryList3(Map map);
      	public List<Employee> queryByIf(Employee emp);
      }

       

  14. 以下是:insert,update 和 delete 语句的示例:
    1. <insert id="addEmp">
      	insert into employee (ename,ePass,eSex,eAge,eTel,eAddr) values (#{eName},#{ePass},#{eSex},#{eAge},#{eTel},#{eAddr})
      </insert>	
      <!-- 注意标签,如果是修改需要使用的标签是update -->
      <update id="updateEmp">
      	<!-- 注意,这里使用的字段名称要和数据库一直,使用#取值要和实体类一致 -->
      	update employee set ename = #{eName}, eaddr = #{eAddr} where eid = #{eid}
      </update>	
      <delete id="deleteEmp">
      	delete from employee where eid = #{id}
      </delete>

       

  15. 增加、修改、删除的Java代码如下:
    1. public void add(EmployeeDao employeeDao){
      	Employee emp = new Employee();
      	emp.seteName("张三");
      	emp.seteAddr("海南");
      	emp.seteAge(20);
      	emp.setePass("123123");
      	emp.seteTel("0108899876");
      	employeeDao.addEmp(emp);
      }
    2. public void update(EmployeeDao employeeDao){
      	Employee emp = new Employee();
      	emp.setEid(10019);
      	emp.seteName("李四111");
      	emp.seteAddr("新家坡");
      	employeeDao.updateEmp(emp);
      }
    3. public void delete(EmployeeDao employeeDao){
      	employeeDao.deleteEmp(10015);
      	employeeDao.deleteEmp(10016);
      	employeeDao.deleteEmp(10017);
      }
  16. 多条件查询的XML配置
    1. <!-- 多条件查询,String name char sex -->
      <select id="queryList" resultType="com.company.bean.Employee">
      	<!-- #{}默认采用预处理方式去处理Sql语句 -->
      	<!-- ${}默认采用 非预处理方式去处理数据 -->
      	select * from employee where ename like '${param1}%' and esex = #{param2}
      </select>
      <select id="queryList2" resultType="com.company.bean.Employee">
      	select * from employee where ename like '${name}%' and esex = #{sex}
      </select>
      <!-- 使用Map对象传递 -->
      <select id="queryList3" resultType="com.company.bean.Employee">
      	select * from employee where ename like '${name}%' and esex = #{sex}
      </select>

       

  17. 多条件查询的Java代码
    1. //创建接口对象,这个是session对象,通过动态代理自动代理帮我们创意的
      EmployeeDao employeeDao = sqlSession.getMapper(EmployeeDao.class);
      //List<Employee> list = employeeDao.queryList("李", '男');
      //List<Employee> list = employeeDao.queryList2("李", '男');//使用注解的方式给参数起别名称
      //使用Map方式
      Map map = new HashMap<>();
      map.put("name", "李");
      map.put("sex", '男');
      List<Employee> list = employeeDao.queryList3(map);
      

       

  18. 多条件查询,条件不固定XML配置
    1. <select id="queryByIf" resultType="com.company.bean.Employee">
      	select * from employee where
      	<if test="eName!=null">
      		ename like '${eName}%'
      	</if>
      </select>

       

  19. 使用注解方式实现
    1. Dao类的注解方式
      1. public interface EmployeeDao2 {
        	@Select("select * from employee")
        	public List<Employee> selectAll();		
        	@Select("select * from Employee where eid = #{id}")	
        	public Employee selectOne(int id);
        }
    2. 在configuration.xml配置文件中增加对使用注解的接口扫描
      1. <mapper class="com.home.dao.EmployeeDao2"/>
    3. Java类中调用

      1. 在Java中调用和XML方式一致
  20. 模糊查询之:条件不固定
    1. <select id="queryIf" resultType="com.company.bean.Employee">
      	select * from employee where 1=1
      	<if test="eName != null">
      		and ename like '${eName}%'
      	</if>
      </select>
  21. 动态SQL
  22. Java接口中的定义:
    1. public interface EmployeeDao {
      	public List<Employee> queryIf(Employee emp);
      	public List<Employee> queryChoose(Employee emp);	
      	public List<Employee> queryByTrim(Employee emp);
      	public void update(Employee emp);
      	public int deleteByList(List<Integer> list);	
      	public int deleteByArray(int[] arrays);
      }

       

  23. 多条件的模糊查询
    1. <!-- select * from employee where 1=1 and ename like '张%' -->
      <select id="queryChoose" resultType="com.company.bean.Employee">
      	select * from employee where 1=1
      	<choose>
      		<when test="eName != null">
      			and ename like '${eName}%'
      		</when>
      		<when test="eAddr != null">
      			and eAddr like '${eAddr}%'
      		</when>
      		<otherwise>
      			order by ename;
      		</otherwise>
      	</choose>
      </select>

       

  24. 多条件查询,避免使用where 1=1
    1. <!-- 意思是,如果是以and开始,那么就用where来替换and或者or -->
      <select id="queryByTrim" resultType="com.company.bean.Employee">
      	select * from employee
      	<trim prefix="where" prefixOverrides="AND|OR">
      		<if test="eName != null">
      			and ename like '${eName}%'
      		</if>
      		<if test="eAddr != null">
      			and eAddr like '${eAddr}%'
      		</if>
      	</trim>
      </select>

       

  25. 实现多条件更新
    1. <!-- update employee SET eName = ?, eAddr = ? where eid = ?  -->
      <update id="update">
      	update employee
      	<set>
      		<if test="eName!=null">eName = #{eName},</if>
      		<if test="ePass!=null">ePass = #{ePass},</if>
      		<if test="eAddr!=null">eAddr = #{eAddr}</if>
      	</set>
      	where eid = #{eid}
      </update>

       

  26. 集合参数传递
    1. 实现批量删除操作:集合参数传递
      1. <!-- delete from employee where eid in ( ? , ? )  -->
        <delete id="deleteByList">
        	delete from employee where eid in 
        	<foreach item="eid" collection="list" open="(" separator="," close=")">
        		#{eid}
        	</foreach>
        </delete>

         

    2. Java中的实现
      1. List<Integer> list = new ArrayList<>();
        list.add(10020);
        list.add(10021);
        int i = employeeDao.deleteByList(list);
        

         

  27. 数组参数传递
    1. 实现批量操作:数据参数传递
      1. <!-- delete from employee where eid in ( ? , ? )  -->
        <!-- 注意:这里collection中的参数必须是array,不是arrays -->
        <delete id="deleteByArray">
        	delete from employee where eid in
        	<foreach item="item" collection="array" open="(" separator="," close=")">
        		#{item}
        	</foreach>
        </delete>

         

    2. Java中代码实现
      1. int[] arrays = {10022,10023};
        int i = employeeDao.deleteByArray(arrays);

         

  28. ResultMap
    1. resultMap 元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBC ResultSets 数据提取代码中解放出来, 并在一些情形下允许你做一些 JDBC 不支持的事情。 实际上,在对复杂语句进行联合映射的时候,它很可能可以代替数千行的同等功能的代码。 ResultMap 的设计思想是,简单的语句不需要明确的结果映射,而复杂一点的语句只需要描述它们的关系就行了。
  29. Java数据访问接口
    1. public interface EmployeeDao {
      	public List<Employee> selectAll();
      	public List<Employee> selectAll2();
      	public List<Employee> selectAll3();
      	public List<Employee> selectAll4();
      	public Employee selectStep(int id);
      }

       

  30. JavaBean代码

    1. public class Employee {
      	private int id;
      	private String name;
      	private String address;
      }

       

  31. 映射文件

    1. 第一种方式

      1. <!-- 第一种方式,起别名称,实体类和数据库表字段不一致的情况 -->
        <select id="selectAll" resultType="com.company.bean.Employee">
        	select eid as id, ename as name, eaddr as address from employee
        </select>

         

    2. 第二种方式
      1. <!-- 第二种方式,使用resultMap -->
        <resultMap type="com.company.bean.Employee" id="resultMap1">
        	<id column="eid" property="id"/>
        	<result column="ename" property="name"/>
        	<result column="eaddr" property="address"/>
        </resultMap>
        <select id="selectAll2" resultMap="resultMap1">
        	select * from employee
        </select>

         

    3. 第三种方式
      1. <resultMap type="com.company.bean.Employee" id="resultMap2">
        	<id column="eid" property="id"/>
        	<result column="ename" property="name"/><!-- column中指定查出来字段的名称 -->
        	<result column="eaddr" property="address"/>
        	<result column="d_id" property="dept.id"/>
        	<result column="d_name" property="dept.name"/>
        </resultMap>
        <select id="selectAll3" resultMap="resultMap2">
        	select e.eid eid, e.ename ename, e.eaddr eaddr,d.did d_id, d.dname d_name from employee e,department d where e.did = d.did
        </select>

         

    4. Java代码中增加一个部门属性
      1. private Department dept;

         

    5. 第四种方式,联表查询,使用association
      1. <resultMap type="com.company.bean.Employee" id="resultMap3">
        	<id column="eid" property="id"/>
        	<result column="ename" property="name"/><!-- column中指定查出来字段的名称 -->
        	<result column="eaddr" property="address"/>
        	<association property="dept" javaType="com.home.bean.Department">
        		<id column="d_id" property="id"/>
        		<result column="d_name" property="name"/>
        	</association>
        </resultMap>
        <select id="selectAll4" resultMap="resultMap3">
        	select e.eid eid, e.ename ename, e.eaddr eaddr,d.did d_id, d.dname d_name from employee e,department d where e.did = d.did
        </select>

         

  32. ResultMap的属性
    1. resultMap 元素有很多子元素和一个值得讨论的结构。 下面是 resultMap 元素的概念视图。
    2. constructor - 用于在实例化类时,注入结果到构造方法中
    3. idArg - ID 参数;标记出作为 ID 的结果可以帮助提高整体性能
    4. arg - 将被注入到构造方法的一个普通结果
    5. id – 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能
    6. result – 注入到字段或 JavaBean 属性的普通结果
    7. association – 一个复杂类型的关联;许多结果将包装成这种类型
    8. 嵌套结果映射 – 关联可以指定为一个 resultMap 元素,或者引用一个
    9. collection – 一个复杂类型的集合
    10. 嵌套结果映射 – 集合可以指定为一个 resultMap 元素,或者引用一个
    11. discriminator – 使用结果值来决定使用哪个 resultMap
    12. case – 基于某些值的结果映射
    13. 嵌套结果映射 – 一个 case 也是一个映射它本身的结果,因此可以包含很多相 同的元素,或者它可以参照一个外部的 resultMap。
  33. 分步查询
    1.  <resultMap type="com.company.bean.Employee" id="resultMap4">
      	<id column="eid" property="id"/>
      	<result column="ename" property="name"/><!-- column中指定查出来字段的名称 -->
      	<result column="eaddr" property="address"/>
      	<!-- 想要完成分步查询 还要使用association 里面不用再次设定(前提是你的实体类和数据库表字段一一对应) -->
      	<association property="dept" select="com.home.dao.DepartmentDao.findDempartmentById" column="did">
      	</association>
       </resultMap>
       <select id="selectStep" resultMap="resultMap4">
      	select * from employee where eid = #{id}
       </select>
    2. <mapper namespace="com.company.dao.DepartmentDao">
      	<select id="findDempartmentById" resultType="com.company.bean.Department">
      		select did as id,dname as name from Department where did=#{did}
      	</select>
      </mapper>

       

  34. 什么是延迟加载
    1. 需求:如果查询订单并且关联查询用户信息。如果先查询订单信息即可以满足要求,当我们需要查询用户信息时候再查询用户信息。把对用户的信息按照需要去查询就是延迟加载。
    2. 延迟加载:先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。
  35. 配置延迟加载,在config.xml中设置为延迟加载,注意配置顺序
    1. <!-- 配置文件的顺序不能变,配置延迟加载 settings 需要在properties节点的下面-->
      <settings>
      	 <!--打开延迟加载的开关  -->
          <setting name="lazyLoadingEnabled" value="true"/>
          <!--将积极加载改为消极加载及按需加载  -->
      </settings>
      

       

  36. Java单元测试:
    1. Employee employee = employeeDao.selectStep(10008);
      System.out.println(employee.getName());
      String dn = employee.getDept().getName();
      System.out.println(dn);

       

  37. 分步查询log4j日志中展示的内容:
    1. [23:29:23:393] [DEBUG] - org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) - ==>  Preparing: select * from employee where eid = ? 
      [23:29:23:584] [DEBUG] - org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) - ==> Parameters: 10008(Integer)
      [23:29:23:875] [DEBUG] - org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) - <==      Total: 1
      李小鹏
      [23:29:23:877] [DEBUG] - org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) - ==>  Preparing: select did as id,dname as name from Department where did=? 
      [23:29:23:878] [DEBUG] - org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) - ==> Parameters: 1(Integer)
      [23:29:24:052] [DEBUG] - org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:159) - <==      Total: 1
      研发部

       

  38. 延迟加载的配置
    1. Mybati默认没有开启延迟加载,需要在总配置文件中setting配置。
    2. 在MyBatis核心配置文件中配置:
      • lazyLoadingEnabled、aggressiveLazyLoading
  39. 小结
    1. 使用延迟加载方法,先去查询简单的sql(最好单表,也可以关联查询),再去按需要加载关联查询的其它信息。
  40. MyBatis的cache缓存
    1. 什么是缓存
      • 缓存就是计算机的一块存储区域,把数据放入到存储区域中,读取速度快。
    2. MyBatis的缓存
      • MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。MyBatis3 中的缓存实现的很多改进都已经实现了,使得它更加强大而且易于配置。
      • 默认情况下是没有开启缓存的,除了局部的 session 缓存,可以增强变现而且处理循环 依赖也是必须的。要开启二级缓存,你需要在你的 SQL 映射文件中添加一行:
      • <cache/>
    3. 什么是一级缓存,为什么使用一级缓存?
      • 每当我们使用MyBatis开启一次和数据库的会话,MyBatis会创建出一个SqlSession对象表示一次数据库会话。
      • 在对数据库的一次会话中,我们有可能会反复地执行完全相同的查询语句,如果不采取一些措施的话,每一次查询都会查询一次数据库,而我们在极短的时间内做了完全相同的查询,那么它们的结果极有可能完全相同,由于查询一次数据库的代价很大,这有可能造成很大的资源浪费。
      • 为了解决这个问题,减少资源浪费,MyBatis会在表示会话的SQLSession对象中建立一个简单的缓存,将每次查询到的结果缓存起来,当下次查询的时候,如果判断现有个完全一样的查询,会直接从缓存中将结果取出来,返回给用户,不需要再进行一次查询数据库了。
    4. 一级缓存
      • 如下图所示,MyBatis会在一次会话的表示:一个SqlSession的对象创建一个本地缓存(LocalCache),对每一次查询,都会尝试根据查询的条件去本地缓存中查找是否存在,如果在缓存中,就直接从缓存中取出,然后返回给用户;否则,从数据库读取数据,将查询结果存入缓存并返回给用户。
      • 对于会话(Session)级别的数据缓存,我们称之为一级数据缓存,简称一级缓存。
    5. MyBatis中的一级缓存是怎样组织的?(即SqlSession中的缓存是怎样组织的?)
      1. SqlSession对象、Executor对象、Cache对象之间的关系如下图所示:
      2. PerpetualCache实现原理其实很简单,其内部就是通过一个简单的HashMap<k,v>来实现的,没有其他的任何限制。
      3. 如下是:PepetualCache的代码实现:
      4. public class PerpetualCache implements Cache {  
        	private String id;  
        	private Map<Object, Object> cache = new HashMap<Object, Object>(); 
        	public void putObject(Object key, Object value) {  
        		cache.put(key, value);  
        	}  
        	public Object getObject(Object key) {  
        		return cache.get(key);  
        	}  
        	public Object removeObject(Object key) {  
        		return cache.remove(key);  
        	}  
        	public void clear() {  
        		cache.clear();  
        	}
        }

         

      5. 一级缓存的生命周期有多长?
        1. d.SqlSession中执行了任何一个update操作(update()、delete()、insert()),都会清空PerpetualCache对象的数据,但是该对象可以继续使用;
        2. c.如果SqlSession调用了clearCache(),会清空PerpetualCache对象中的数据,但是该对象仍可使用;
        3. b.如果SqlSession调用了close()方法,会释放掉一级缓存PerpetualCache对象,一级缓存将不可用;
        4. a.MyBatis在开启一个数据库会话时,会创建一个新的SqlSession对象,SqlSession对象中会有一个新的Executor对象,Executor对象中持有一个新的PerpetualCache对象;当会话结束时,SqlSession对象及其内部的Executor对象还有PerpetualCache对象也一并释放掉。
      6. SQLSession一级缓存的工作流程
        1. 结束
        2. 如果没命中:
          1. 去数据库中查询数据,得到查询的结果;
          2. 将key和查询到的结果分别作为key,value对,存储到Cache中;
          3. 将查询结果返回给用户。
        3. 如果命中,则直接将缓存结果返回;
        4. 判断从Cache中根据特定的key值取的数据数据是否为空,即是否命中;
        5. 对于某个查询,根据statementId,params,rowBounds来构建一个key值,根据这个key值去缓存Cache中取出对应的key值存储的缓存结果;
      7. Cache中Map的key值:CacheKey
        1. MyBatis认为,对于两次查询,如果以下条件都完全一样,那么就认为它们是完全相同的两次查询:
          1. 传入的statementId对于MyBatis而言,你要使用它,必须需要一个statementId,它代表着你将执行什么样的Sql;
          2. 查询时要求的结果集中的结果范围(结果的范围通过rowBounds.offset和rowBounds.limit表示);
          3. 这次查询所产生的最终要传递给JDBC java.sql.Preparedstatement的Sql语句字符串(boundSql.getSql())
          4. 传递给java.sql.Statement要设置的参数值
          5. 综上所述,CacheKey由以下条件决定:statementId +rowBounds +传递给JDBC的SQL +传递给JDBC的参数值
        2. 也可以这样说:如何确定Cache中的key值?
        3. 换句话说就是:怎样判断某两次查询是完全相同的查询?
        4. 现在最核心的问题出现了:怎样来确定一次查询的特征值?
        5. 我们知道,Cache最核心的实现其实就是一个Map,将本次查询使用的特征值作为key,将查询结果作为value存储到Map中。
      8. MyBatis的缓存机制整体设计以及二级缓存的工作模式
        1. MyBatis的二级缓存是Application级别的缓存,它可以提高对数据库查询的效率,以提高应用的性能。
      9. CachingExecutor是Executor的装饰者,以增强Executor的功能,使其具有缓存查询的功能,这里用到了设计模式中的装饰者模式。
      10. 要想使某条Select查询支持二级缓存,你需要保证:
        • 1.MyBatis支持二级缓存的总开关:全局配置变量参数cacheEnabled=true
        • 2.该select语句所在的Mapper,配置了<cache> 或<cached-ref>节点,并且有效
        • 3.该select语句的参数useCache=true
        • 4.对象的类必须实现序列化接口
      11. 一级缓存和二级缓存的使用顺序 :
        • 二级缓存 ———> 一级缓存 ———> 数据库
      12. 二级缓存作用域为 Mapper(Namespace);
        • configuration.MappedStatement.Cache;项目启动时会初始化;
      13. Cache的XML配置
        • 所有的这些属性都可以通过缓存元素的属性来修改。
        • <cache 	eviction="FIFO" 	flushInterval="60000" 		size="512"	 	readOnly="true"/>

           

        • eviction默认的是 LRU。
          flushInterval(刷新间隔)可以被设置为任意的正整数,而且它们代表一个合理的毫秒形式的时间段。默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。
          size(引用数目)可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的 可用内存资源数目。默认值是 1024。
          readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。可读写的缓存会返回缓存对象的拷贝(通过序列化) 。这会慢一些,但是安全,因此默认是 false

           

        • eviction默认的是 LRU。
          flushInterval(刷新间隔)可以被设置为任意的正整数,而且它们代表一个合理的毫秒形式的时间段。默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。
          size(引用数目)可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的 可用内存资源数目。默认值是 1024。
          readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。可读写的缓存会返回缓存对象的拷贝(通过序列化) 。这会慢一些,但是安全,因此默认是 false

           

  41. Cache使用时的注意事项/避免使用二级缓存
    1. 注意事项
      • 只能在【只有单表操作】的表上使用缓存,不只是要保证这个表在整个系统中只有单表操作,而且和该表有关的全部操作必须全部在一个namespace下。
      • 在可以保证查询远远大于insert,update,delete操作的情况下使用缓存这一点不需要多说,所有人都应该清楚。记住,这一点需要保证在1的前提下才可以!
    2. 避免使用二级缓存
      • 可能会有很多人不理解这里,二级缓存带来的好处远远比不上他所隐藏的危害。
        1. 缓存是以namespace为单位的,不同namespace下的操作互不影响。
        2. insert,update,delete操作会清空所在namespace下的全部缓存。
        3. 通常使用MyBatis Generator生成的代码中,都是各个表独立的,每个表都有自己的namespace。
  42. 使用MyBatis的二级缓存有三个选择:
    1. MyBatis自身提供的缓存实现;
    2. 用户自定义的Cache接口实现;(集成Cache 接口)
    3. 跟第三方内存缓存库的集成;
  43. 建议了解nosql数据库redis

觉得有点用记得给我点赞哦!

这篇文章就到这里啦,如果觉得有收获的话,不妨给一个赞喔;文章中涉及的内容肯定是不全面的,欢迎大家评论区留言补充。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值