首页 > 数据库 >MyBatis保姆级理解与使用,动态SQL(核心)

MyBatis保姆级理解与使用,动态SQL(核心)

时间:2022-08-27 15:47:56浏览次数:168  
标签:缓存 二级缓存 EmpMapper SqlSession 保姆 emp SQL MyBatis Emp

1. 动态SQL(核心)

1.1 简介

Mybatis框架的动态SQL技术是一种根据特定条件动态拼装SQL语句的功能,它存在的意义是为了解决拼接SQL语句字符串时的难点问题。

比如: 我们在多条件查询的时候会写这样的语句:

select * from sys_user where 1=1 and

 

再比如:做更新的时候,我们没有修改的数据列也执行了更新操作。

1.2 if和where标签

<!-- List<Emp> select ByCondition(Emp emp); -->

 

<select id="selectByCondition" resultType="com.hy.bean.Emp">

 

    select emp_id,emp_name,emp_salary from sys_emp

    <!-- where标签会自动去掉“标签体内前面、后面多余的and/or” -->

    <where>

        <!-- 使用if标签,让我们可以有选择的加入SQL语句的片段。这个SQL语句片段是否要加入整个SQL语句,就看if标签判断的结果是否为true -->

        <!-- 在if标签的test属性中,可以访问实体类的属性,不可以访问数据库表的字段 -->

        <if test="empName != null">

            <!-- 在if标签内部,需要访问接口的参数时还是正常写#{} -->

 

            or emp_name=#{empName}

        </if>

        <if test="empSalary > 2000">

            or emp_salary>#{empSalary}

        </if>

 

        <!--

         第一种情况:所有条件都满足 WHERE emp_name=? or emp_salary>?

         第二种情况:部分条件满足 WHERE emp_salary>?

         第三种情况:所有条件都不满足 没有where子句

         -->

    </where>

</select>

 

 

1.3 set标签

需求:实际开发时,对一个实体类对象进行更新。往往不是更新所有字段,而是更新一部分字段。此时页面上的表单往往不会给不修改的字段提供表单项 。

 

例如:上面的表单,如果服务器端接收表单时,使用的是User这个实体类,那么userName、userBalance、userGrade接收到的数据就是null 。

如果不加判断,直接用User对象去更新数据库,在Mapper配置文件中又是每一个字段都更新,那就会把userName、userBalance、userGrade设置为null值,从而造成数据库表中对应数据被破坏。

此时需要我们在Mapper配置文件中,对update语句的set子句进行定制,此时就可以使用动态SQL的set标签。

 


<!-- void updateeDynamic(Emp emp) -->

 

<update id="updateEmployeeDynamic">

 

    update sys_emp

 

    <!-- set emp_name=#{empName},emp_salary=#{empSalary} -->

 

    <!-- 使用set标签动态管理set子句,并且动态去掉两端多余的逗号 -->

 

    <set>

 

        <if test="empName != null">

 

            emp_name=#{empName},

 

        </if>

 

        <if test="empSalary < 3000">

 

            emp_salary=#{empSalary},

 

        </if>

 

    </set>

 

    where emp_id=#{empId}

 

   </update>

         第一种情况:所有条件都满足 SET emp_name=?, emp_salary=?

         第二种情况:部分条件满足 SET emp_salary=?

         第三种情况:所有条件都不满足 update t_emp where emp_id=?

                没有set子句的update语句会导致SQL语法错误

 

 

1.4 trim标签

使用trim标签控制条件部分两端是否包含某些字符

prefix属性:指定要动态添加的前缀内容

suffix属性:指定要动态添加的后缀

prefixOverrides属性:指定要动态去掉的前缀,使用“|”分隔有可能的多个值

suffixOverrides属性:指定要动态去掉的后缀,使用“|”分隔有可能的多个值

 

下面例子中,trim标签内部如果有条件,则where会出现,否则where不出现。

suffixOverrides=“and|or”:整个条件部分,如果在后面有多出来的“and或or”会被自动去掉。

 

<!-- List<Emp> selectByConditionByTrim(Emp emp) -->

<select id="selectByConditionByTrim" resultType="com.hy.bean.Emp">

    select emp_id,emp_name,emp_age,emp_salary,emp_gender

    from sys_emp

   <!-- prefix属性指定要动态添加的前缀 -->

    <!-- suffix属性指定要动态添加的后缀 -->

    <!-- prefixOverrides属性指定要动态去掉的前缀,使用“|”分隔有可能的多个值 -->

    <!-- suffixOverrides属性指定要动态去掉的后缀,使用“|”分隔有可能的多个值 -->

    <!-- 当前例子用where标签实现更简洁,但是trim标签更灵活,可以用在任何有需要的地方 -->

    <trim prefix="where" suffixOverrides="and|or">

        <if test="empName != null">

            emp_name=#{empName} and

        </if>

        <if test="empSalary > 3000">

            emp_salary > #{empSalary} and

        </if>

        <if test="empAge < 20">

            emp_age=#{empAge} or

        </if>

        <if test="empGender==’ m’ ">

            emp_gender=#{empGender}

        </if>

    </trim>

</select>

 

 

 

1.5 choose/when/otherwise标签

在多个分支条件中,仅执行一个。

<!-- List<Emp> selectByConditionByChoose(Emp emp) -->

<select id="selectEmployeeByConditionByChoose" resultType="com.hy.bean.Emp">

    select emp_id,emp_name,emp_salary from sys_emp

    where

    <choose>

        <when test="empName != null">emp_name=#{empName}</when>

        <when test="empSalary > 3000">emp_salary > 3000</when>

        <otherwise>1=1</otherwise>

    </choose>

    

    <!--

     第一种情况:第一个when满足条件 where emp_name=?

     第二种情况:第二个when满足条件 where emp_salary < 3000

     第三种情况:两个when都不满足 where 1=1 执行了otherwise

     -->

</select>

 

 

1.6 foreach标签

1.6.1 基本用法

比如:批量插入

abstract public void insertBatch(@Param(“empList”) List<Emp> empList));

<!--  

INSERT INTO `sys_emp` VALUES (null, '李冰冰', 'lbb', 'f', 300,default,2),

(null, '张彬彬', 'zbb', 'm', 599,default,3),

(null, '万茜', 'wq', 'm', 4000,default,1),

(null, '李若彤', 'lrt', 'm', 5000.8,default,1)

-->

<insert id="insertBatch">

INSERT INTO `sys_emp`(emp_id,emp_name,emp_gender)

<foreach collection="empList" item="emp" separator="," open="VALUES" >

(null,#{emp.empName},#{emp.empGender})

</foreach>

</insert>

 

 

我们这里写的批量插入的例子本质上是一条SQL语句。

    collection属性:要遍历的集合,如果接口中方法中使用了@Param ,collection属性中要用这个名字。

    item属性:遍历集合的过程中能得到每一个具体对象,在item属性中设置一个名字,将来通过这个名字引用遍历出来的对象

    separator属性:指定当foreach标签的标签体重复拼接字符串时,各个标签体字符串之间的分隔符

    open属性:指定整个foreach循环把字符串拼好后,这个字符串整体的前面要添加的字符串

    close属性:指定整个foreach循环把字符串拼好后,这个字符串整体的后面要添加的字符串

    index属性:这里起一个名字,便于后面引用

        遍历List集合,这里能够得到List集合的索引值

        遍历Map集合,这里能够得到Map集合的key

 

如果接口形参位置没有使用@Param注解,而且foreach标签也没有使用默认的名称,则会抛出异常

 

Caused by:org.apache.ibatis.binding.BindingException: Parameter ‘empList’ not found. Available parameters are[arg0,collection, list]

1.6.2 批量更新

而实现批量更新则需要多条update SQL语句拼起来,并且用分号分开。也就是一次性发送多条SQL语句让数据库执行。

此时需要在数据库连接信息的URL地址中设置

?allowMultiQueries=true

 

对应的foreach标签如下:

<!-- int updateBatch(@Param("empList") List<Emp> empList) -->

<update id="updateBatch">

    <foreach collection="empList" item="emp" separator=";">

        update sys_emp set emp_name=#{emp.empName} where emp_id=#{emp.empId}

    </foreach>

</update>

 

 

1.6.3 关于foreach标签的collection属性

如果没有给接口中List类型的参数使用@Param注解指定一个具体的名字,那么在collection属性中默认可以使用collection或list来引用这个list集合。这一点可以通过异常信息看出来:

Parameter 'empList' not found. Available parameters are [collection, list]

 

在实际开发中,为了避免隐晦的表达造成一定的误会,建议使用@Param注解明确声明变量的名称,然后在foreach标签的collection属性中按照@Param注解指定的名称来引用传入的参数。

 

 

1.7 sql标签

 

1.7.1 抽取重复的SQL片段

 

 <!-- 使用sql标签抽取重复出现的SQL片段 -->

    <sql id="mySelectSql">

        select emp_id,emp_name,emp_age,emp_salary,emp_gender from t_emp

    </sql>

 

1.7.2 引用已抽取的SQL片段

 <!-- 使用include标签引用声明的SQL片段 -->

 <include refid="mySelectSql"/>

 

 

2.缓存

2.1 缓存机制

 

 

 

 

比如:以前农村装水的水缸

 

2.2 一级缓存和二级缓存

 

 

 

 

二级缓存被所有的SqlSession共享,也就是能被访问的。

一级缓存是SqlSession级别的缓存。在操作数据库时需要构造 sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。

二级缓存是Mapper(namespace)级别的缓存。多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。

 

第一次查询,先将结果放入到一级缓存,等SqlSession提交事务后,再将数据放入到二级缓存。

 

2.2.1 一级缓存和二级缓存的使用顺序

 

查询的顺序是:

1)先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用。

2)如果二级缓存没有命中,再查询一级缓存

3)如果一级缓存也没有命中,则查询数据库

SqlSession关闭之前,会将一级缓存中的数据会写入二级缓存

 

2.2.2 效用范围

一级缓存:SqlSession级别

二级缓存:SqlSessionFactory级别

 

 

 

 

 

 

 

 

它们之间范围的大小参考下面图:

 

 

 

 

一个请求中可能包含多个事务  ------  一个service方法(一个SqlSession)对应一个事务

注意:缓存是用再查询过程中的,增删改,反而会破坏缓存。造成缓存和数据库不一致,所以很多情况,执行了一个增删改操作,他会把缓存清空。

 

3. 一级缓存

Mybatis默认开启了一级缓存

3.1 案例1:测试一级缓存是否存在

为了测试缓存失效,我们需要修改测试类中的代码。需要关闭SqlSessioin对象,然后重新开,再重新开的话,需要操作SqlSessionFactory。

 

如何测试一级缓存是否存在???同一个数据查两次,但是只发了一条SQL语句[给数据库]。

3.1.1 证明一级缓存存在

 

package com.hy.mybatis.test;

 

import java.io.IOException;

 

import org.apache.ibatis.io.Resources;

import org.apache.ibatis.session.SqlSession;

import org.apache.ibatis.session.SqlSessionFactory;

import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import org.junit.Before;

import org.junit.Test;

 

import com.hy.bean.Emp;

import com.hy.mapper.EmpMapper;

 

public class TestLevelOneCache1 {

private SqlSessionFactory sqlSessionFactory;

private Logger logger = null;

@Before

public void init() throws IOException {

logger = LoggerFactory.getLogger(this.getClass());

sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));

}

 

@Test

public void testLevelOneCache1_01() throws IOException {

SqlSession session = sqlSessionFactory.openSession();

EmpMapper empMapper = session.getMapper(EmpMapper.class);

 

Emp emp1 = empMapper.selectById(1);

Emp emp2 = empMapper.selectById(1); // 第二次是从缓存中查询出来的。

 

logger.info("是否相等:"+ (emp1 == emp2)); //true

 

session.commit();

session.close();

}

}

 

只发一条SQL语句。一级缓存默认开启,不需要做额外的配置。二级缓存需要开启才有,现在没有开,所以只有一级缓存。

 

 

 

3.2 案例2:一级缓存失效的情况:

1)同一个SqlSession两次查询期间提交了事务

2)同一个SqlSession两次查询期间执行了任何一次增删改操作,不论是够提交事务,都清空一级缓存

3)同一个SqlSession但是查询条件发生了变化

4)同一个SqlSession两次查询期间手动清空了缓存

5)不是同一个SqlSession

 

总结:在执行commit,rollback时会清空一级缓存

一级缓存的清除还有以下两个地方:

 

1、就是获取缓存之前会先进行判断用户是否配置了flushCache=true属性(参考一级缓存的创建代码截图),如果配置了则会清除一级缓存。

2、MyBatis全局配置属性localCacheScope配置为Statement时,那么完成一次查询就会清除缓存。

 

3.2.1 同一个SqlSession两次查询期间提交了事务

public class TestLevelCache1 {

private SqlSessionFactory sqlSessionFactory;

private Logger logger = null;

@Before

public void init() throws IOException {

logger = LoggerFactory.getLogger(this.getClass());

sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));

}

 

//1) 同一个SqlSession两次查询期间提交了事务

//1.1 没有提交事务,使用一级缓存

@Test

public void testLevelOneCache1_01 () throws IOException {

SqlSession session = sqlSessionFactory.openSession();

EmpMapper empMapper = session.getMapper(EmpMapper.class);

 

Emp emp1 = empMapper.selectById(1);

Emp emp2 = empMapper.selectById(1); // 第二次是从缓存中查询出来的。

 

logger.info("是否相等:"+ (emp1 == emp2)); //true

 

session.commit();

session.close();

}

 

 

//1.2 两次查询中间,提交了事务,清空一级缓存

@Test

public void testLevelOneCache1_02() throws IOException {

SqlSession session = sqlSessionFactory.openSession();

EmpMapper empMapper = session.getMapper(EmpMapper.class);

 

Emp emp1 = empMapper.selectById(1);

session.commit();

Emp emp2 = empMapper.selectById(1); // 由于进行了事务的提交,所以又发送了一条查询语句。

 

logger.info("是否相等:"+ (emp1 == emp2)); //false

session.close();

}

 

 

 

 

}

 

 

3.2.2 同一个SqlSession两次查询期间执行了任何一次增删改操作,并且提交了事务

 

 

 

 

 

@Test

public void testLevelOneCache01_4() {

SqlSession sqlSession = sqlSessionFactory.openSession();

 

EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class);

 

Emp emp1 = empMapper.selectById(1);

 

//Emp newEmp = new Emp(2L, "范冰冰ILoveYou", null, null, null, null);

//empMapper.updateById(newEmp);  //更新但是不提交事务

 

empMapper.deleteById(8);  //执行了删除没有,提交事务也,重新一遍

 

Emp emp2 = empMapper.selectById(1);

//logger.debug(emp2.toString());

logger.debug("是否相等:" + (emp1 == emp2));

 

sqlSession.commit();

sqlSession.close();

}

 

 

// 2.2

@Test

public void testLevelOneCache2_02() throws IOException {

SqlSession session = sqlSessionFactory.openSession();

 

EmpMapper empMapper = session.getMapper(EmpMapper.class);

 

Emp emp1 = empMapper.selectById(1); //查询后,将对象放入一级缓存

 

empMapper.deleteById(7); //执行了delete操作,但是并没有提交

session.commit();

 

Emp emp11 = empMapper.selectById(1); //提交了事务,一级缓存清空

logger.info("是否相等:"+ (emp1 == emp11)); //false

 

session.commit();

session.close();

}

 

 

 

 

 

 

第一次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,如果没有,从数据库查询用户信息,将查询到的用户信息存储到一级缓存中。

如果中间sqlSession去执行插入、更新、删除,并且commit,清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。

第二次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,缓存中有,直接从缓存中获取用户信息。如果没有,则重新发出一条查询语句。

 

3.2.3 同一个SqlSession但是查询条件发生了变化

// 3. 同一个SqlSession但是查询条件发生了变化

@Test

public void testLevelOneCache3_01() throws IOException {

SqlSession session = sqlSessionFactory.openSession();

 

EmpMapper empMapper = session.getMapper(EmpMapper.class);

 

Emp emp1 = empMapper.selectById(1); //查询后,将对象放入一级缓存

//查询的是emp_id为2的员工信息,一级缓存中并没有所以重新发出一条SQL语句

Emp emp2 = empMapper.selectById(2);

session.commit();

session.close();

}

 

 

 

 

3.2.4 同一个SqlSession两次查询期间手动清空了缓存

// 4)同一个SqlSession两次查询期间手动清空了缓存

@Test

public void testLevelOneCache4() throws IOException {

SqlSession session = sqlSessionFactory.openSession();

 

EmpMapper empMapper = session.getMapper(EmpMapper.class);

 

Emp emp1 = empMapper.selectById(1);

session.clearCache();

Emp emp2 = empMapper.selectById(1); // 由于两次查询中间清空了一级缓存,所以又发送了一条查询语句。

logger.info("是否相等:"+ (emp1 == emp2)); //false

 

session.commit();

session.close();

}

 

 

3.2.5 不是同一个SqlSession

 

// 5)不是同一个SqlSession

@Test

public void testLevelOneCache1_05() throws IOException {

SqlSession session01 = sqlSessionFactory.openSession();

SqlSession session02 = sqlSessionFactory.openSession();

EmpMapper empMapper01 = session01.getMapper(EmpMapper.class);

EmpMapper empMapper02 = session02.getMapper(EmpMapper.class);

Emp emp1 = empMapper01.selectById(1);

// 由于是不同的SqlSession,所以又发送了一条查询语句。各自有各自的一级缓存。

Emp emp2 = empMapper02.selectById(1);

 

logger.info("是否相等:"+ (emp1 == emp2)); //false

session01.commit();

session02.commit();

session01.close();

session02.close();

}

 

 

 

 

 

4. 二级缓存[mybatis自带的二级缓存]

 

 

 

 

 

4.1 使用二级缓存步骤

1)在想要使用二级缓存的EmpMapper映射文件中加入cache标签

开启全局二级缓存配置:<setting name="cacheEnabled" value="true"></setting> 

 

2)让实体类支持序列化

public class Emp implements Serializable{

private static final long serialVersionUID = 1L;

 

这里我们使用mybaits自带的二级缓存,后面我们使用第三方的二级缓存EHCache。

 

3)SqlSession提交事务时才会将查询到的数据存入二级缓存

@Test

public void testLevelTwoCache() {

// 测试二级缓存存在:使用两个不同SqlSession执行查询

// 说明:SqlSession提交事务时才会将查询到的数据存入二级缓存

// 所以本例并没有能够成功从二级缓存获取到数据

SqlSession sqlSession01 = sqlSessionFactory.openSession();

SqlSession sqlSession02 = sqlSessionFactory.openSession();

 

EmpMapper empMapper01 = sqlSession01.getMapper(EmpMapper.class);

EmpMapper empMapper02 = sqlSession02.getMapper(EmpMapper.class);

 

//[00:01:07.699] [DEBUG] [main] [com.hy.mapper.EmpMapper] [Cache Hit Ratio [com.hy.mapper.EmpMapper]: 0.0]

Emp emp01 = empMapper01.selectById(1);

//[00:01:07.746] [DEBUG] [main] [com.hy.mapper.EmpMapper] [Cache Hit Ratio [com.hy.mapper.EmpMapper]: 0.0]

Emp emp02 = empMapper02.selectById(1);

 

logger.info("是否相等:"+ (emp1 == emp2)); //false

sqlSession01.commit();

sqlSession01.close();

 

sqlSession02.commit();

sqlSession02.close();

}

 

 

修改代码;

@Test

public void testSecondLevelCache2() {

SqlSession sqlSession01 = sqlSessionFactory.openSession();

SqlSession sqlSession02 = sqlSessionFactory.openSession();

 

EmpMapper empMapper01 = sqlSession01.getMapper(EmpMapper.class);

EmpMapper empMapper02 = sqlSession02.getMapper(EmpMapper.class);

 

Emp emp01 = empMapper01.selectById(1);

 

sqlSession01.commit();

sqlSession01.close();

Emp emp02 = empMapper02.selectById(1);

 

//false 注意,从二级缓存中new了一个新的对象,所以emp01和emp02不是同一个对象

logger.info("是否相等:"+ (emp1 == emp2));

sqlSession02.commit();

sqlSession02.close();

}

 

 

 

 

虽然两个对象不相等,但是注意:只发出了一条查询语句。

 

日志中打印的Cache Hit Ratio叫做缓存命中率

缓存命中率=命中缓存的次数/查询的总次数

 

4)mybatis序列化的特点

mybatis的二级缓存是属于序列化,序列化的意思就是从内存中的数据传到硬盘中,这个过程就是序列化;

反序列化意思就是相反而已;

也就是说,mybatis的二级缓存,实际上就是将数据放进了硬盘文件中去了;

 

现在呢,你仅仅的将Emp类给序列化了,如果有父类Person、级联属性,它们是不会跟着被序列化的,所以光这些是不够的;

很简单,如果Emp需要序列化,但是这个类中还有其他类的属性,仅需将其他类也实现序列化接口即可!

比如Emp类继承了Person父类,那么父类也需要实现Serializable这个接口;

4.2 二级缓存失效的条件

与一级缓存一样,二级缓存也会存在失效的条件的,下面我们就来探究一下哪些情况会造成二级缓存失效。

4.2.1 第一次SqlSession 未提交

SqlSession 在未提交的时候,SQL 语句产生的查询结果还没有放入二级缓存中,这个时候 SqlSession2 在查询的时候是感受不到二级缓存的存在的

4.2.2 更新对二级缓存影响

与一级缓存一样,更新操作对二级缓存造成影响,下面用三个 SqlSession来进行模拟,第一个 SqlSession 只是单纯的查询并提交,第二个 SqlSession 用于查询二级缓存是否失效,第三个 SqlSession2 用于执行更新操作。

@Test

public void testLevelTwoCache01_03() {

SqlSession sqlSession1 = sqlSessionFactory.openSession();

SqlSession sqlSession2 = sqlSessionFactory.openSession();

SqlSession sqlSession3 = sqlSessionFactory.openSession();

 

EmpMapper empMapper1 = sqlSession1.getMapper(EmpMapper.class);

EmpMapper empMapper2 = sqlSession2.getMapper(EmpMapper.class);

EmpMapper empMapper3 = sqlSession3.getMapper(EmpMapper.class);

 

 

Emp emp1 = empMapper1.selectById(1);

logger.info(emp1.toString());

sqlSession1 .commit();  //清空一级缓存,加入二级缓存

 

Emp emp2  = empMapper2.selectById(1);

logger.info(emp2.toString());  //从二级缓存中查询出来

logger.info("是否是同一个对象:"+(emp1==emp2));

sqlSession2.commit();

 

Emp newEmp = new Emp(1L,"垃圾张文宏",null,null,null,null);

empMapper3.updateById(newEmp);

sqlSession3.commit();

 

Emp emp22  = empMapper2.selectById(1);

logger.info(emp22.toString());  //从二级缓存中查询出来

logger.info("是否是同一个对象:"+(emp1==emp22));

sqlSession2.commit();

 

 

sqlSession1.close();

sqlSession2.close();

}

 

因为所有的 insert,delete,uptede 都会触发缓存的刷新,从而导致二级缓存失效,所以二级缓存适合在读多写少的场景中开启。

 

1)  每个增删改标签都有默认清空缓存配置:flushCache="true",不过这是默认的是一级和二级缓存都清空

2)每个select标签都默认配置了useCache="true":
         如果useCache= false:则表示不使用缓存(一级缓存依然使用,二级缓存不使用)

 

4.2.3 测试时的问题

测试的时候,sqlSessionFactory不同,自然也会导致二级缓存失效 。

 

4.2.4 测试时的问题

@Test

public void testLevelTwoCache04() {

SqlSession sqlSession01 = sqlSessionFactory.openSession();

SqlSession sqlSession02 = sqlSessionFactory.openSession();

SqlSession sqlSession03 = sqlSessionFactory.openSession();

 

EmpMapper empMapper01 = sqlSession01.getMapper(EmpMapper.class);

EmpMapper empMapper02 = sqlSession02.getMapper(EmpMapper.class);

EmpMapper empMapper03 = sqlSession03.getMapper(EmpMapper.class);

 

Emp emp01 = empMapper01.selectById(1);

System.out.println(emp01);

Emp newEmp = new Emp(1L, "范冰冰", "fbbfbb", null, null, null, null,null);

empMapper02.updateByCondition(newEmp);

 

sqlSession02.commit();

sqlSession02.close();

 

Emp emp02 = empMapper01.selectById(1);

System.out.println(emp02);

 

System.out.println(emp01 == emp02);

}

 

 [com.hy.mapper.EmpMapper] [Cache Hit Ratio [com.hy.mapper.EmpMapper]: 0.0]

 [com.hy.mapper.EmpMapper.selectById] [==>  Preparing: select * from sys_emp where emp_id = ?]

 [com.hy.mapper.EmpMapper.selectById] [==> Parameters: 1(Long)]

 [com.hy.mapper.EmpMapper.selectById] [<==  Total: 1]

Emp [empId=1, empName=范冰冰222, empPwd=fbb, empGender=f, empSalary=30000.0, empStatus=y

 [com.hy.mapper.EmpMapper.updateByCondition] [==>  Preparing: update sys_emp SET emp_name = ? where emp_id = ?]

 [com.hy.mapper.EmpMapper.updateByCondition] [==> Parameters: 范冰冰(String), 1(Long)]

 [com.hy.mapper.EmpMapper.updateByCondition] [<==    Updates: 1]

 [com.hy.mapper.EmpMapper] [Cache Hit Ratio [com.hy.mapper.EmpMapper]: 0.0]

Emp [empId=1, empName=范冰冰222, empPwd=fbb, empGender=f, empSalary=30000.0, empStatus=y

true

 

 

 

4.3 二级缓存应该开启吗

一级缓存默认是开启的,而二级缓存是需要我们手动开启的,那么我们什么时候应该开启二级缓存呢?

 

1、因为所有的(insert,delete,uptede)都会触发缓存的刷新,从而导致二级缓存失效,所以二级缓存适合在读多写少的场景中开启。

 

2、因为二级缓存针对的是同一个namespace,所以建议是在单表操作的Mapper中使用,或者是在相关表的Mapper文件中共享同一个缓存。

 

 

 

 

5 EHCache缓存

 

 

 

 

5.1 EHCache简介

第三方的缓存产品

官网地址:https://www.ehcache.org/

 

 

 

 

 

5.2 整合操作

<!-- Mybatis EHCache整合包 -->

<dependency>

    <groupId>org.mybatis.caches</groupId>

    <artifactId>mybatis-ehcache</artifactId>

    <version>1.2.1</version>

</dependency>

 

<dependency>

<groupId>ch.qos.logback</groupId>

<artifactId>logback-classic</artifactId>

<version>1.2.3</version>

</dependency>

 

 

2.5.2.1 Mybatis环境

在Mybatis环境下整合EHCache,前提当然是要先准备好Mybatis的环境。

<project xmlns="http://maven.apache.org/POM/4.0.0"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>

<groupId>com.hy</groupId>

<artifactId>springmvc006</artifactId>

<version>0.0.1</version>

<packaging>war</packaging>

 

<properties>

<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

<maven.compiler.encoding>UTF-8</maven.compiler.encoding>

</properties>

 

<dependencies>

<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->

<dependency>

<groupId>org.projectlombok</groupId>

<artifactId>lombok</artifactId>

<version>1.18.20</version>

</dependency>

 

<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->

<!-- 数据库连接池 -->

<dependency>

<groupId>com.alibaba</groupId>

<artifactId>druid</artifactId>

<version>1.2.9</version>

</dependency>

 

<dependency>

<groupId>com.fasterxml.jackson.core</groupId>

<artifactId>jackson-databind </artifactId>

<version>2.12.1</version>

</dependency>

 

<!-- mybaits相关jar包 -->

<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->

<dependency>

    <groupId>org.mybatis</groupId>

    <artifactId>mybatis</artifactId>

    <version>3.5.6</version>

</dependency>

 

<!-- Mybatis EHCache整合包 -->

<dependency>

    <groupId>org.mybatis.caches</groupId>

    <artifactId>mybatis-ehcache</artifactId>

    <version>1.2.1</version>

</dependency>

 

<!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic -->

<!--

logback日志

slf4j日志门面的一个具体实现

 -->

<dependency>

<groupId>ch.qos.logback</groupId>

<artifactId>logback-classic</artifactId>

<version>1.2.3</version>

</dependency>

 

<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<version>4.12</version>

<scope>test</scope>

</dependency>

 

<dependency>

<groupId>mysql</groupId>

<artifactId>mysql-connector-java</artifactId>

<version>5.1.49</version>

</dependency>

<!-- mybaits相关jar包 -->

</dependencies>

 

<build>

<plugins>

<!-- 指定jdk,防止update project -->

<plugin>

<groupId>org.apache.maven.plugins</groupId>

<artifactId>maven-compiler-plugin</artifactId>

<configuration>

<source>1.8</source>

<target>1.8</target>

<!-- 项目编码 -->

<encoding>UTF-8</encoding>

</configuration>

</plugin>

</plugins>

</build>

</project>

 

这是一个EHCache和mybatis的整合包,但是会讲EHCache核心包传递进来。

 

 

 

 

2.5.2.2 各主要jar包

 

 

 

 

作用

 

 

2.5.2.3 整合EHCache

 

 

 

创建EHCache配置文件, ehcache.xml

 

 

 

 

 

 

 

<?xml version="1.0" encoding="utf-8" ?>

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

         xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">

    <!-- 磁盘保存路径 -->

    <diskStore path="f:\hy\ehcache"/>

    

    <defaultCache

            maxElementsInMemory="1000"

            maxElementsOnDisk="10000000"

            eternal="false"

            overflowToDisk="true"

            timeToIdleSeconds="120"

            timeToLiveSeconds="120"

            diskExpiryThreadIntervalSeconds="120"

            memoryStoreEvictionPolicy="LRU">

    </defaultCache>

</ehcache>

 

 

 

 

 

2.5.2.4 指定缓存管理器的具体类型

还是到查询操作所的EmpMapper配置文件中,找到之前设置的cache标签:

 

 

 

 

 

 

 

logback.xml

<?xml version="1.0" encoding="UTF-8"?>

<configuration debug="true">

    <!-- 指定日志输出的位置 -->

    <appender name="STDOUT"

              class="ch.qos.logback.core.ConsoleAppender">

        <encoder>

            <!-- 日志输出的格式 -->

            <!-- 按照顺序分别是:时间、日志级别、线程名称、打印日志的类、日志主体内容、换行 -->

            <pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%msg]%n</pattern>

        </encoder>

    </appender>

 

    <!-- 设置全局日志级别。日志级别按顺序分别是:TRACE > DEBUG > INFO > WARN > ERROR > FATAL -->

    <!-- 指定任何一个日志级别都只打印当前级别和后面级别的日志。 -->

    <root level="DEBUG">

        <!-- 指定打印日志的appender,这里通过“STDOUT”引用了前面配置的appender -->

        <appender-ref ref="STDOUT" />

    </root>

 

    <!-- 根据特殊需求指定局部日志级别 -->

    <logger name="com.hy.mapper" level="DEBUG" />

</configuration>

 

 

 

 

 

 

 

 

5.3 EHCache配置文件说明

 

 改成10秒,10秒后释放。

 

 

标签:缓存,二级缓存,EmpMapper,SqlSession,保姆,emp,SQL,MyBatis,Emp
From: https://www.cnblogs.com/lijili/p/16596849.html

相关文章

  • mysql逻辑结构,存储结构
    宏观:库,存储在操作系统目录中表:  微观:段区页一个表就是一个段,mysql分配空间时至少分配一个区,每个区默认时1M(64个page页),mysql最小的IO单元就是PAGE(16KB) ......
  • mysql常用操作汇总
    工作中经常用会遇到这种情况,可以访问mysql所在的服务器,但是服务器端口不对外暴露(通常因为安全原因)。这时,操作数据库只能通过命令行和mysqlclient窗口来实现。我对这些操作......
  • sql里修改表字段的sql脚本
    红色字体时表明,蓝色字体时列名--添加列ALTERTABLEBa_CustomerLinkmanAddressADDCarryCategoryintGOEXECsys.sp_addextendedproperty@name=N'MS_Description',@v......
  • mysql执行时的sql_mode设置
    查询当前MySQL的模式:select@@sql_mode;  根据需要设置模式,这里是将ONLY_FULL_GROUP_BY去掉:SETSESSIONsql_mode=(SELECTREPLACE(@@sql_mode,'ONLY_FULL_GROUP......
  • sql里将所有表结构生成数据字典的sql脚本
    SELECT表名=CASEWHENa.colorder=1THENd.nameELSE''END,表说明=CASEWHENa.colorder=1THENisnull(f.value,'')ELSE''END,--字段序号=a.colorder,字段名=a.name......
  • SQL:排名前几名中的前几名
    1.选择订单量排名前2的商品,并且选出每个商品订单量排名前3的销售方式,模拟数据如下:name作为商品,count作为对应渠道的订单量,way为渠道  通过excel法线,前两名存在3个:......
  • 数据库学习笔记 (本数据库学习笔记以SQL sever 2019 为例进行学习) 20220823 第一节课
    教材及参考数据库课程讲什么?内容安排第一部分数据库原理部分第一章数据库系统概述为什么要学习数据库?数据库的发展改变了人们的工作和生活模式信息积累与运用......
  • mysql-开启日志记录功能
    开启日志记录功能--开启功能SETGLOBALgeneral_log=ON;--保存到文件SETGLOBALlog_output='file';查看日志内容--查看日志保存位置及开启状态showvariab......
  • MySQL十六:36张图理解Buffer Pool
    转载~在应用系统中,我们为加速数据访问,会把高频的数据放在「缓存」(Redis、MongoDB)里,减轻数据库的压力。在操作系统中,为了减少磁盘IO,引入了「缓冲池」(bufferpool)机制......
  • MySQL十七:Change Buffer
    转载~在之前的文章《InnoDB的存储结构》介绍的InnoDB的存储结构的组成中,我们知道ChangeBuffer也是用InnoDB内存结构的组成部分。ChangeBuffer主要是为了在写入是减少......