Mybatis

目录

概述

MyBatis的环境搭建 

测试 

API接口说明 

面向接口开发需要遵循的规范

 Mybatis日志

 参数传递

结果处理

对象映射

#{}和${}区别

特殊处理使用resultMap

定义resultMap

使用resultMap

注意事项

多表关联处理结果集

嵌套查询

注解方式

常用注解标签

使用案例 

Mybatis动态SQL

if元素 

where元素

​编辑 

 trim元素

 choose元素

set元素 

set元素实现代码

trim元素实现代码

foreach元素

特殊符号处理

MyBatis的一级缓存和二级缓存

为什么使用缓存

一级缓存

一级缓存工作模式

​编辑

一级缓存演示代码

二级缓存

二级缓存配置操作

二级缓存工作模式

二级缓存演示代码

MyBatis架构


概述

MyBatis是一款优秀的半自动的ORM持久层框架,它支持自定义SQL,存储过程以及高级映射

MyBatis支持几乎所有的JDBC代码以及设置参数和获取结果集的工作

MyBatis将基本的JDBC常用接口封装,对外提供操作即可。

MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

MyBatis是一款优秀的持久层框架。

 那么什么是持久层呢?

持久层:可以立即保存在磁盘上

什么是ORM?

ORM(Object Relation Mapping),对象关系映射。对象指的是Java对象,关系指的是数据库中的关系模型,对象关系映射,指的就是在Java对象和数据库的关系模型之间建立一种对应的关系。例如,Java中的一个Student类,来对应数据库中的一张Student表,类中的属性和表中的列一一对应。Student类就对应Student表,一个Student对象就对应student表中的一行数据。 

MyBatis的环境搭建 

 第一步.创建一张表和表对应的实体类

这里我创建了一个Student类和student表来举例

package com.ffyc.mybatis.model;

import java.io.Serializable;

public class Student implements Serializable {


    private Integer id;
    private Integer num;//建议不使用基本类型 建议使用包装类型 默认值是null
    private String name;
    private String gender;
    private Major major;//类与类之间的关联关系 has-a 什么有什么

    public Major getMajor() {
        return major;
    }

    public void setMajor(Major major) {
        this.major = major;
    }

    public Integer getId() {
        return id;
    }

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

    public Integer getNum() {
        return num;
    }

    public void setNum(Integer num) {
        this.num = num;
    }

    public String getName() {
        return name;
    }

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

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", num=" + num +
                ", name='" + name + '\'' +
                ", gender='" + gender + '\'' +
                ", major=" + major +
                '}';
    }
}

第二步.导入MyBatis jar包和mysql数据驱动包

 <!--mybatis-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.2</version>
        </dependency>

 <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.16</version>
        </dependency>

这里建议大家可以将这些常用的jar包整理起来,方便以后得使用。

第三步.创建MyBatis全局配置文件

需要将其放在一个名为mybatis.xml的文件中

<?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"><!--dtd文件是用来制定xml标签规范的-->
<configuration>
    <environments default="development">
        <environment id="development">
            <!--
              事务管理方式:就是一次对数据库操作过程中,执行多条sql管理
        
              把所有的操作都成功执行后,再提交事务,让数据库最终执行本次提交的所有sql
            -->
            <!--事务管理方式-->
            <transactionManager type="JDBC"/><!--使用jdbc事务管理-->
            <!--数据库连接池配置
                频繁地创建销毁与数据库的连接对象是比较占用时间和空间
                可以在池子中默认创建若干个连接对象,有请求使用时,直接从连接池中取出一个对象,用完再还回去,
                减少了创建和销毁的时间开销
             -->
            <dataSource type="POOLED">
                <!--数据库连接-->
                <property name="driver" value="com.mysql.cj.jdbc.Driver" />
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/ssmdb?serverTimezone=Asia/Shanghai" />
                <property name="username" value="root" />
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
</configuration>

 第四步.创建sql映射文件

sql映射文件放在resources目录下创建一个mapper目录 创建一个StudentMapper.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"><!--dtd文件 叫xml的约束文件,定义xml标签-->
<mapper namespace="定义接口地址">
    sql语句
</mapper>

在我们搭建好MyBatis环境后,我们需要测试一下。

定义一个接口StudentDao

package com.ffyc.mybatis.dao;

import com.ffyc.mybatis.model.Student;

import java.util.List;

public interface StudentDao {
    List<Student> findStudents(Student student);
}

测试 

我们可以查询一下我们在数据库中存储的学生信息

可以使用以下的代码来测试一下

package com.ffyc.mybatis.test;

import com.ffyc.mybatis.dao.AdminDao;
import com.ffyc.mybatis.model.Admin;
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 javax.xml.ws.soap.Addressing;
import java.io.IOException;
import java.io.Reader;
import java.util.List;

public class Test1 {
    public static void main(String[] args) throws IOException {
        //读入mybatis核心配置文件
        Reader reader =  Resources.getResourceAsReader("mybatis.xml");
        //创建SqlSessionFactory对象
        SqlSessionFactory sqlSessionFactory =  new SqlSessionFactoryBuilder().build(reader);
        //创建SqlSession对象
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //为接口创建一个代理对象
        StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
        //调用我们自己的方法

        List<Student> studentList = studentDao.findStudents();
        System.out.println(studentList);

        sqlSession.commit();//提交数据库事务 事务只针对新增 修改 删除操作 查询不需要提交事务的
        //关闭与数据库的会话对象
        sqlSession.close();
    }
}

读取MyBatis核心配置文件
Reader reader =  Resources.getResourceAsReader("mybatis.xml");

创建SqlSessionFactory对象

SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);

创建SqlSession对象

SqlSession sqlSession =sqlSessionFactory.openSession();

为接口创建一个代理对象

StudentDao studentDao = sqlSession.getMapper(StudentDao.class);

API接口说明 

SqlSessionFactory 接口

使用 SqlSessionFactory 来创建 SqlSession,一旦创建 SqlSessionFactory就会在整个应用过程中始终存在。由于创建开销较大,所以没有理由去销毁再创建它,一个应用运行中也不建议多次创建 SqlSessionFactory。

SqlSession 接口

Sqlsession 意味着创建与数据库链接会话,该接口中封装了对数据库操作的方法,与数据库会话完成后关闭会话。

 由于SqlSessionFactory接口在整个过程中只会创建一次,所以为了简化我们在项目中的代码,我们可以把其创建过程放在一个静态代码块里。

具体的代码如下 我们可以创建一个MybatisUtil的Class文件 

package com.ffyc.mybatis.util;

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 java.io.IOException;
import java.io.Reader;

public class MybatisUtil {
    static SqlSessionFactory sqlSessionFactory = null;
    static {
        //读入mybatis核心配置文件
        Reader reader = null;
        try {
            reader = Resources.getResourceAsReader("mybatis.xml");
        } catch (IOException e) {
            e.printStackTrace();
        }
       /*
           创建SqlSessionFactory对象
           SqlSessionFactory对象是用来创建SqlSession对象的
           由于SqlSessionFactory对象创建的开销较大,所以一个项目中只创建一个SqlSessionFactory对象,不用关闭
       */
       sqlSessionFactory =  new SqlSessionFactoryBuilder().build(reader);
    }

    public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession();
    }
}

在以后得测试代码中,我们就可以向以下的代码这样写

package com.ffyc.mybatis.test;

import com.ffyc.mybatis.dao.StudentDao;
import com.ffyc.mybatis.model.Student;
import com.ffyc.mybatis.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;

import java.util.List;

public class Test3 {
    public static void main(String[] args) {
        /*
       SqlSession对象是与数据库交互的,每次与数据库连接都需要创建一个新的连接对象
       用完关闭即可
       */
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        //获取接口的代理对象,由代理对象调用接口中对应方法所匹配的sql
        StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
        /*Student student = studentDao.findStudentById(1);
        System.out.println(student);*/
       /* List<Student> students = studentDao.findStudents();
        System.out.println(students);*/
       /* Student student = studentDao.findStudentById1(1);
        System.out.println(student);*/


        sqlSession.commit();
        sqlSession.close();
    }
}

这样的代码会更加简洁一些。

面向接口开发需要遵循的规范

1、 Mapper.xml 文件中的 namespace 与 mapper 接口的类路径相同

2、 Mapper 接口方法名和 Mapper.xml 中定义的每个 statement 的 id 相同。

3、 Mapper 接口方法的输入参数类型和 mapper.xml 中定义的每个 sql 的

parameterType 的类型相同。

4、 Mapper 接口方法的输出参数类型和 mapper.xml 中定义的每个 sql 的

resultType 的类型相同。

 Mybatis日志

具体选择哪个日志实现由 MyBatis 的内置日志工厂确定。它会使用最先找到的。

Mybatis 内置的日志工厂提供日志功能,具体的日志实现有以下几种方式:

SLF4J|LOG4J|JDK_LOGGINGCOMMONS_LOGGING|STDOUT_LOGGING

配置方法

 这个需要放在我们创建的StudentMapper文件中。

<settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/><!--打印执行日志-->
</settings>

 参数传递

参数传递分为单个参数直接传递和多个参数使用@Param("id")绑定

单个参数直接传递

Admin findAdminById(int id);

多个参数使用@Param("id")绑定

Admin login(@Param("account") String account,@Param("password") String password);

具体代码如下

单个参数直接传递

<select id="findAdminById" parameterType="int" resultType="Admin">
        select * from admin where id = #{id}
</select>

 多个参数使用@Param("id")绑定

 <select id="login" resultType="Admin">
        select * from admin where account = #{account} and password = #{password}
 </select>

如果传入一个复杂的对象,就需要使用paramterType参数进行类型定义:

例如:

void updateAdmin(Admin admin);

结果处理

对象映射

#{}和${}区别

#{}占位符,是经过预编译的,编译好SQL语句在取值,#方式能够防止sql注入

     #{}:delete from admin where id = #{id}

     结果:deletee from admin where id = ?

${}会将值以字符串形式拼接到sql语句,${}方式无法防止sql注入

     ${}:delete from admin where id = '${id}'

     结果:delete from admin where id = '1'

一般是#{}向sql传值使用,而使用${}向sql传列名

例如在 order by ${column} 语句后面可以动态替换列名

特殊处理使用resultMap

定义resultMap

 <resultMap id="adminmap" type="Admin">
        <id property="id" column="id"></id><!--映射主键的-->
        <result property="account" column="account"></result>
        <result property="password" column="password"></result>
        <result property="gender" column="gender"></result>
        <result property="adminAge" column="admin_age"></result>
  </resultMap>

使用resultMap

 <select id="findAdminByAccount" parameterType="string" resultMap="adminmap">
        select * from admin where account = #{account}
 </select>

注意事项

1.resultMap的id属性是resultMap的唯一标识,本例中定义为"adminmap"

2.resultMap的type属性是映射的POJO类型

3.id标签映射主键,result标签映射非主键

4.property设置对象的属性名称,column映射查询结果的列名称

多表关联处理结果集

此处需要用到resultMap元素中association,collection元素

Collection元素处理一对多关联

这里我用学生和专业的关系来举例

专业与学生一对多关系

专业一方,配置多方集合

public class Major {
    private Integer id;
    private String name;
    private List<Student> students;
}

学生多方,在多方配置一方

public class Student implements Serializable {


    private Integer id;
    private Integer num;//建议不使用基本类型 建议使用包装类型 默认值是null
    private String name;
    private String gender;
    private Major major;//类与类之间的关联关系 has-a 什么有什么
}

 使用resultMap组装查询结果

<!--自定义结果映射关系-->
    <resultMap id="studentMap" type="Student"><!--最终返回一个学生对象 将学生信息封装学生对象-->
        <id property="id" column="id"></id><!--映射主键的-->
        <result property="num" column="num"></result>
        <result property="name" column="name"></result>
        <result property="gender" column="gender"></result>
        <association property="major" javaType="Major">
            <result property="name" column="mname"></result>
        </association>
    </resultMap>
    <select id="findStudentById" parameterType="int" resultMap="studentMap">
SELECT
 s.id,
 s.num,
 s.name,
 s.gender,
 m.name mname
FROM
 student s LEFT JOIN major m ON s.majorid = m.id
WHERE
 s.id = #{id}
    </select>

嵌套查询

将一个多表关联查询拆分为多次查询 先查询主表数据,然后查询关联表数据。

<!--嵌套查询 把一个关联查询分成两个单表查询,然后通过学生专业外键,查询关联专业信息-->
    <resultMap id="studentMap1" type="Student">
        <association property="major" javaType="Major" select="findMajorById" column="majorid"></association>
    </resultMap>
    <select id="findStudentById1" parameterType="int" resultMap="studentMap1">
SELECT
 s.id,
 s.num,
 s.name,
 s.gender,
 s.majorid
FROM
 student s
WHERE
 s.id = #{id}
    </select>
    <select id="findMajorById" parameterType="int" resultType="Major">
        SELECT
         *
        FROM
         major
        WHERE
         id = #{id}
    </select>

注解方式

常用注解标签

@Insert : 插入 sql , 和 xml insert sql 语法完全一样

@Select : 查询 sql, 和 xml select sql 语法完全一样

@Update : 更新 sql, 和 xml update sql 语法完全一样

@Delete : 删除 sql, 和 xml delete sql 语法完全一样

@Param : 入参

@Results : 设置结果集合

@Result : 结果

使用案例 

Mybatis动态SQL

可以根据具体的参数条件,来对SQL语句进行动态拼接。

if元素 

if标签可以对传入的条件进行判断

            <if test="name!=null">
                name = #{name},
            </if>
            <if test="num!=null">
                num = #{num},
            </if>
            <if test="gender!=null">
                gender = #{gender}
            </if>

where元素

where元素会进行判断,如果他包含的标签中有返回值的话,它就插入一个'where'

此外,如果标签返回的内容是以AND 或 OR开头,它会剔除掉AND 或 OR

 <select id="findStudents" resultType="Student">
 select * from student
 <where>
     <if test="name!=null">
         name = #{name}
     </if>
     <if test="num!=0">
         and num = #{num}
     </if>
     <if test="gender!=null">
         and gender = #{gender}
     </if>
 </where>
     </select>

我们可以测试一下 看看运行时候的sql语句

package com.ffyc.mybatis.test;

import com.ffyc.mybatis.dao.StudentDao;
import com.ffyc.mybatis.model.Student;
import com.ffyc.mybatis.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;

import java.util.ArrayList;
import java.util.List;

public class Test5 {
    public static void main(String[] args) {
        /*
       SqlSession对象是与数据库交互的,每次与数据库连接都需要创建一个新的连接对象
       用完关闭即可
       */
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        //获取接口的代理对象,由代理对象调用接口中对应方法所匹配的sql
        StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
        Student student = new Student();
        student.setName("张泽兴");
        student.setGender("男");
        student.setNum(20221808);
        List<Student> students = studentDao.findStudents(student);
        System.out.println(students);
        sqlSession.commit();
        sqlSession.close();
    }
}

我们可以看到运行时的sql语句为

 

 trim元素

让我们添加一个指定的前缀关键字,让我们去除指定的关键字

<!--trim 让我们添加一个指定的前缀关键字
     让我们去除指定的关键字-->
   <select id="findStudents" resultType="Student">
        select * from student
        <trim prefix="where" prefixOverrides="and|or">
            <if test="name!=null">
                name = #{name}
            </if>
            <if test="num!=0">
                and num = #{num}
            </if>
            <if test="gender!=null">
                and gender = #{gender}
            </if>
        </trim>
    </select>

测试代码

package com.ffyc.mybatis.test;

import com.ffyc.mybatis.dao.StudentDao;
import com.ffyc.mybatis.model.Student;
import com.ffyc.mybatis.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;

import java.util.ArrayList;
import java.util.List;

public class Test5 {
    public static void main(String[] args) {
        /*
       SqlSession对象是与数据库交互的,每次与数据库连接都需要创建一个新的连接对象
       用完关闭即可
       */
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        //获取接口的代理对象,由代理对象调用接口中对应方法所匹配的sql
        StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
        Student student = new Student();
        student.setName("张泽兴");
        student.setGender("男");
        student.setNum(20221808);
        List<Student> students = studentDao.findStudents(student);
        System.out.println(students);
        sqlSession.commit();
        sqlSession.close();
    }
}

sql语句

 choose元素

有时候我们不想使用所有的条件,而只是想从多个条件中选择一个使用。

choose元素有点像我们在JavaSE中学到的switch语句

  <select id="findStudents" resultType="Student">
    select * from student
        <where>
            <choose>
                <when test="name!=null">
                    name = #{name}
                </when>
                <when test="num!=0">
                    and num = #{num}
                </when>
                <otherwise>
                    name = '李钰轩'
                </otherwise>
            </choose>
        </where>
    </select>

测试代码:

package com.ffyc.mybatis.test;

import com.ffyc.mybatis.dao.StudentDao;
import com.ffyc.mybatis.model.Student;
import com.ffyc.mybatis.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;

import java.util.ArrayList;
import java.util.List;

public class Test5 {
    public static void main(String[] args) {
        /*
       SqlSession对象是与数据库交互的,每次与数据库连接都需要创建一个新的连接对象
       用完关闭即可
       */
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        //获取接口的代理对象,由代理对象调用接口中对应方法所匹配的sql
        StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
        Student student = new Student();
        student.setNum(20221807);
        List<Student> students = studentDao.findStudents(student);
        System.out.println(students);
        sqlSession.commit();
        sqlSession.close();
    }
}

这里我们传入的student对象里有学生的学号,所以我们查询的结果就是为此学号的学生信息

 如果我们传入的student对象里的学生学号或者学生姓名为null 这时就会查询<otherwise></otherwise> 里设置的条件

package com.ffyc.mybatis.test;

import com.ffyc.mybatis.dao.StudentDao;
import com.ffyc.mybatis.model.Student;
import com.ffyc.mybatis.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;

import java.util.ArrayList;
import java.util.List;

public class Test5 {
    public static void main(String[] args) {
        /*
       SqlSession对象是与数据库交互的,每次与数据库连接都需要创建一个新的连接对象
       用完关闭即可
       */
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        //获取接口的代理对象,由代理对象调用接口中对应方法所匹配的sql
        StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
        Student student = new Student();
        student.setNum(0);
        student.setName(null);
        List<Student> students = studentDao.findStudents(student);
        System.out.println(students);
        sqlSession.commit();
        sqlSession.close();
    }
}

以下为运行结果:

set元素 

set元素可以把最后一个逗号去掉

这个功能trim元素也可以实现

set元素实现代码
<!--
    <set> 动态添加set关键字 还可以去掉最后的逗号
    -->
   <update id="updateStudent" parameterType="student">
        update student
        <set>
            <if test="name!=null">
                name = #{name},
            </if>
            <if test="num!=null">
                num = #{num},
            </if>
            <if test="gender!=null">
                gender = #{gender}
            </if>
        </set>
        where id = #{id}
    </update>

测试代码

<update id="updateStudent" parameterType="student">
        update student
        <trim prefix="set" suffixOverrides=",">
            <if test="name!=null">
                name = #{name},
            </if>
            <if test="num!=null">
                num = #{num},
            </if>
            <if test="gender!=null">
                gender = #{gender}
            </if>
        </trim>
        where id = #{id}
 </update>

sql语言

trim元素实现代码
<update id="updateStudent" parameterType="student">
        update student
        <trim prefix="set" suffixOverrides=",">
            <if test="name!=null">
                name = #{name},
            </if>
            <if test="num!=null">
                num = #{num},
            </if>
            <if test="gender!=null">
                gender = #{gender}
            </if>
        </trim>
        where id = #{id}
 </update>

 测试代码

package com.ffyc.mybatis.test;

import com.ffyc.mybatis.dao.StudentDao;
import com.ffyc.mybatis.model.Student;
import com.ffyc.mybatis.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;

import java.util.ArrayList;
import java.util.List;

public class Test5 {
    public static void main(String[] args) {
        /*
       SqlSession对象是与数据库交互的,每次与数据库连接都需要创建一个新的连接对象
       用完关闭即可
       */
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        //获取接口的代理对象,由代理对象调用接口中对应方法所匹配的sql
        StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
        Student student = new Student();
        student.setId(3);
        student.setName("李四");
        sqlSession.commit();
        sqlSession.close();
    }
}

 sql语言

foreach元素

主要用在构建 in 条件中,它可以在 SQL 语句中进行迭代一个集合。foreach

元素的属性主要有 item,index,collection,open,separator,close。

item 表示集合中每一个元素进行迭代时的别名,index 指定一个名字,用于

表示在迭代过程中,每次迭代到的位置,open 表示该语句以什么开始,

separator 表示在每次进行迭代之间以什么符号作为分隔符,close 表示以什

么结束,在使用 foreach 的时候最关键的也是最容易出错的就是 collection

属性,该属性是必须指定的,但是在不同情况下,该属性的值是不一样的。

如果传入的是单参数且参数类型是一个 List 的时候,collection 属 性值为 list

如果传入的是单参数且参数类型是一个 array 数组的时候,collection 的属性值为 array

测试代码

<select id="findStudentsByColumn" resultType="Student">
        select
        <foreach collection="list" item="c" separator=",">
            ${c}
        </foreach>
        from student
    </select>

特殊符号处理

在 mybatis 中的 xml 文件中,存在一些特殊的符号,比如:<、>、"、&、<>等,正常书写 mybatis 会报错,需要对这些符号进行转义。具体转义如下所示:

特殊字符 转义字符

< <

> >

"

"

’ '

&

&

除了可以使用上述转义字符外,还可以使用<![CDATA[]]>来包裹特殊字符。如

下所示:

<if test="id != null">

AND <![CDATA[ id <> #{id} ]]>

</if>

<![CDATA[

]]>是 XML 语法。在 CDATA 内部的所有内容都会被解析器忽略。

但是有个问题那就是 <if> </if> <where> </where>

<choose> </choose> <trim> </trim> 等这些标签都不会被解析,所以

我们只把有特殊字符的语句放在 <![CDATA[

]]> 尽量缩小<![CDATA[ ]]>

的范围

演示代码

<!--
      xml 中特殊符号的处理
      解决方式1:使用转义字符代替
      解决方式2:<![CDATA[ 特殊符号]]>
    -->
    <select id="findStudents1" parameterType="int" resultType="Student">
          select * from student where num <![CDATA[<]]> #{num}
    </select>

MyBatis的一级缓存和二级缓存

为什么使用缓存

缓存的作用是为了减轻数据库的压力,提高查询性能。缓存实现的原理是从数据库中查询出来的对象在使用完后不要销毁,而是存储在内存(缓存)中,当再次需要获取该对象时,直接从内存(缓存)中直接获取,不再向数据库 执行 select 语句,从而减少了对数据库的查询次数,因此提高了数据库的性能。

Mybatis 有一级缓存和二级缓存。一级缓存的作用域是同一个 SqlSession,在同一个 sqlSession 中两次执行相同的 sql 语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。当一个 sqlSession 结束后该 sqlSession 中的一级缓存也就不存在了。Mybatis 默认开启一级缓存。

二级缓存是多个 SqlSession 共享的,其作用域是同一个 namespace,不同的 sqlSession 两次执行相同 namespace 下的 sql 语句且向 sql 中传递参数也相同即最终执行相同的 sql 语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率,Mybatis 默认没有开启二级缓存需要在 setting 全局参数中配置开启二级缓存。

一级缓存

Mybatis 对缓存提供支持,但是在没有配置的默认情况下,它只开启一级缓存,一级缓存只是相对于同一个 SqlSession 而言。所以在参数和 SQL 完全一样的情况下,我们使用同一个 SqlSession 对象调用一个 Mapper 方法,往往只执行一次 SQL,因为使用 SelSession 第一次查询后MyBatis 会将其放在缓存中,以后再查询的时候,如果没有声明需要刷新,并且缓存没有超时的情况下,SqlSession 都会取出当前缓存的数据,而不会再次发送 SQL 到数据库。

一级缓存工作模式
一级缓存演示代码
package com.ffyc.mybatis.test;

import com.ffyc.mybatis.dao.StudentDao;
import com.ffyc.mybatis.model.Student;
import com.ffyc.mybatis.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;

import java.util.ArrayList;

public class Test6 {
    public static void main(String[] args) {
        /*
        mybatis 框架也是提供了缓存功能
        一级缓存:是SqlSession级别的,在同一个Sqlsession中相同的两次查询,只查询一次数据库
                将第一次查询的数据存储在SqlSession对象中,第二次查询直接获取即可

                一级缓存失效
                1.执行增删改操作后,当前的一级缓存会清空
                2.sqlSession.clearCache();强制清空一级缓存
                3.sqlSession.close(); 关闭连接对象,清空一级缓存
        */
        /*
       SqlSession对象是与数据库交互的,每次与数据库连接都需要创建一个新的连接对象
       用完关闭即可
       */
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        //获取接口的代理对象,由代理对象调用接口中对应方法所匹配的sql
        StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
        studentDao.findStudents1(1);//在同一个sqlSession中,再次查询相同的内容,会从一级缓存中查询,不会查询数据库
        //执行删除操作,修改 新增操作后 会清空以及缓存数据
        //sqlSession.clearCache();//强制清空一级缓存
        studentDao.findStudents1(1);
        sqlSession.commit();
        sqlSession.close();
    }
}

我们执行两次一样的查询操作,会发现在最后的输出结果中显示只会进行一次数据库的连接操作,因为以及缓存会将第一次查询的数据存储在SqlSession对象中,第二次查询直接获取即可。

但是如果我们在两次查询操作的中间加上

sqlSession.clearCache()(强制清空一级缓存),这时我们就会发现他会进行两次数据库的连接操作。

二级缓存

二级缓存是 SqlSessionFactory 级别的,根据 mapper 的 namespace 划分区域的,相同 namespace 的 mapper 查询的数据缓存在同一个区域,如果使用 mapper 代理方法每个 mapper 的 namespace 都不同,此时可以理解为二级缓存区域是根据 mapper 划分。 每次查询会先从缓存区域查找,如果找不到则从数据库查询,并将查询到数据写入缓存。Mybatis 内部存储缓存使用一个 HashMap,key 为hashCode+sqlId+Sql 语句。value 为从查询出来映射生成的 java 对象。sqlSession 执行 insert、update、delete 等操作 commit 提交后会清空缓存区域,防止脏读。

二级缓存配置操作

第一步.启用二级缓存

在我们前面创建的mybatis.xml文件中加入

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

 cacheEnabled 设置为 true 时启用二级缓存,设置为 false 时禁用二级缓存。

第二步.对象序列化

在我们创建时实体类对象实现序列化接口java.io.Serialzable

public class Student implements Serializable {
}

第三步.配置映射文件

在我们定义的StudentMapper.xml映射文件中添加<cache />

<!--设置二级缓存配置的
       size="20" 缓存对象数量
       flushInterval=""设置二级缓存的有效时间
   -->
    <cache size="20" flushInterval="3000"></cache>
二级缓存工作模式

二级缓存演示代码
package com.ffyc.mybatis.test;

import com.ffyc.mybatis.dao.StudentDao;
import com.ffyc.mybatis.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;

public class Test7 {
    public static void main(String[] args) {
        /*
        mybatis 框架也是提供了缓存功能
        一级缓存:是SqlSession级别的,在同一个Sqlsession中相同的两次查询,只查询一次数据库
                将第一次查询的数据存储在SqlSession对象中,第二次查询直接获取即可

                一级缓存失效
                1.执行增删改操作后,当前的一级缓存会清空
                2.sqlSession.clearCache();强制清空一级缓存
                3.sqlSession.close(); 关闭连接对象,清空一级缓存
       二级缓存:
             二次缓存是sqlSessionFactory级别(sqlSessionFactory对象只有一个,创建后就不关闭了,多个Sqlsession共享一个sqlSessionFactory)
             使用是需要配置的
             1.启用二级缓存
             <setting name="cacheEnabled" value="true"/>
             2.对象序列化
             将所有的 POJO 类实现序列化接口 Java.io. Serializable
             3.配置映射文件
              在 Mapper 映射文件中添加<cache />,表示此mapper 开启二级缓存

        */
        /*
       SqlSession对象是与数据库交互的,每次与数据库连接都需要创建一个新的连接对象
       用完关闭即可
       */
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        //获取接口的代理对象,由代理对象调用接口中对应方法所匹配的sql
        StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
        studentDao.findStudents1(2);
        sqlSession.commit();
        sqlSession.close();

        SqlSession sqlSession1 = MybatisUtil.getSqlSession();
        //获取接口的代理对象,由代理对象调用接口中对应方法所匹配的sql
        StudentDao studentDao1 = sqlSession1.getMapper(StudentDao.class);
        studentDao1.findStudents1(2);
        sqlSession1.commit();
        sqlSession1.close();
    }
}

因为二级缓存是SqlSessionFactory 级别的,SqlSessionFactory对象只有一个,这里我们创建两个sqlsession对象,用来进行同样的查询操作,会发现在结果中1只进行了一次数据库的连接操作。

MyBatis架构

上一篇:对象存储服务的完整性检查


下一篇:科技助力农业——土壤化肥测试仪