IDEWA项目实践——mybatis的一些基本原理以及案例

系列文章目录

IDEA项目实践——创建Java项目以及创建Maven项目案例、使用数据库连接池创建项目简介

IDEA创建项目的操作步骤以及在虚拟机里面创建Scala的项目简单介绍_intellij 创建scala

IDEA项目实践——动态SQL、关系映射、注解开发

文章目录

系列文章目录

1.MyBatis

1.1 MyBatis入门

1.ORM框架

2.MyBatis简介

4. MyBatis的工作流程和核心对象

5. 配置日志:

1.2 MyBatis接口开发(代理开发)【较为重要】

1. 实现步骤:

1.创建Mapper接口

2.修改mapper.xml文件中的namespace,放到mapper文件夹

3. 修改主配置文件中映射文件的路径

4.测试接口开发

2. MyBatis动态代理原理

3. MyBatisX 插件

1.3 MyBatis核心配置

0.核心配置文件概览

常见配置:

1.properties元素

2.typeAliases

3.mappers

4.settings

1.4 MyBatis映射文件

1.映射文件概述

2.模糊查询总结

1.5 MyBatis缓存

1.一级缓存(本地缓存)【重点了解一级缓存】

演示一级缓存的案例,实现步骤:

1. 添加MyBatis的依赖

2.添加MyBatis的核心配置文件

3.创建POJO - 基于Lombok

4.创建Mapper接口

5.创建Mapper映射文件

6.创建MybatisUtil工具类

7.测试一级缓存

2.二级缓存(全局缓存)

总结


1.MyBatis

1.1 MyBatis入门

1.ORM框架

当今企业级应用的开发环境中,对象和关系数据是业务实体的两种表现形式。业务实体在内存中表现为对象,在数据库中变现为关系数据。当采用面向对象的方法编写程序时,一旦需要访问数据库,就需要回到关系数据的访问方式,这种转换为开发人员带来了很大的麻烦。 ORM框架是一个对象-关系映射的系统化解决方案,当ORM框架完成转换后,开发人员可以直接取用对象。常用的ORM框架有Hibernate和MyBatis,其作用是将数据库查询的数据封装为实体类对象。

ORM框架将数据库查询到的数据封装为实体类对象,ORM映射流程如上图所示。从图中可以看出,实体类与数据库之间通过ORM框架相互映射,应用程序可以直接获取映射完成的实体类。

2.MyBatis简介

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

4. MyBatis的工作流程和核心对象

1.工作流程

MyBatis的工作流程是MyBatis中重要的知识点,整个MyBatis工作流程分为5个步骤。

  1. 编写配置文件与映射文件,其中,配置文件设置数据库连接,映射文件设置与SQL文件相关的操作。

  2. MyBatis通过配置文件和映射文件生成SqlSessionFactory对象,此对象在MyBatis整个生命周期中只存在一份,它负责为每一个连接生成SqlSession对象。

  3. 通过SqlSessionFactory对象生成SqlSession对象,SqlSession对象在每次连接中只有一个,它封装了操作数据库的所有方法。

  4. 在每一次连接中,通过SqlSession对象操作数据库,SqlSession通过底层的Executor执行器执行对应操作。Executor执行器分为两种,一种是普通执行器,另一种是缓存执行器。

  5. Executor执行器将此次操作封装为MappedStatement对象,在执行SQL语句之前,Executor执行器通过MappedStatement对象将输入的实体对象或基本类型数据映射到SQL语句,在执行SQL语句之后,Executor执行器通过MappedStatement对象将SQL语句执行的结果映射为实体对象或基本类型数据

  6. 核心在于将关系型数据映射为MappedStatement对象

2.核心对象

(1)SqlSessionFactory

SqlSessionFactory是MyBatis中的核心类,它采用工厂设计模式构建,负责创建SqlSession对象。构建SqlSessionFactory对象需要使用SqlSessionFactoryBuilder类 调用SqlSessionFactoryBuilder类中的builder()方法即可创建SqlSessionFactory对象 build()方法有多种重载,参数可以选填Reader和InputStream的实现类

 

构建SqlSessionFactory对象

build方法的参数中需要MyBatis配置文件的输入流,接下来创建输入流。调用Resource类的getResourceAsStream()方法,传入配置文件的绝对路径

获得配置文件流之后,将其作为参数传入SqlSessionFactoryBuilder的build()方法中,调用build()方法,返回值就是SqlSessionFactory对象

使用SqlSessionFactory对象

在SqlSessionFactory类中存在openSession()方法与getConfiguration()方法,其中,openSession()方法可以创建SqlSession对象,也可以在该方法中传入参数来设置创建的SqlSession对象,getConfiguration()方法用于获取SqlSessionFactory的配置。

 

产生SqlSession对象

SqlSessionFactory类的主要作用是生产SqlSession对象,调用SqlSessionFactory对象的openSession()方法就可以产生SqlSession对象。 通过SqlSessionFactory对象生成的SqlSession对象,在每次连接中只有一个,它负责通过各种方法操作数据库。

(2)SqlSession

使用SqlSession对象

SqlSession对象是MyBatis中的核心类对象,在日常开发中,常用SqlSession对象与数据库进行交互。除此之外,SqlSession对象贯穿于整个数据库访问的过程,一定时间段内没有使用SqlSession对象时,需要及时调用SqlSession对象的close()方法,将其关闭。 SqlSession对象提供了执行SQL,提交事务或回滚事务,使用映射器等方法,在方法中需要指定映射文件中的方法。

方法名称说明
T selectOne(String var1)执行单条记录的查询操作,需传入执行查询的方法,返回映射的对象
T selectOne(String var1,Object var2)执行单条记录的查询操作,需传入执行查询的方法和参数,返回映射的对象
List<E> selectList(String var1)执行多条记录的查询操作,需传入执行查询的方法,返回查询结果的集合
List<E> selectList(String var1,Object var2)执行多条记录的查询操作,需传入执行查询的方法和参数,返回查询结果的集合
Map<K,V> selectMap(String var1,String var2)执行查询操作,返回一个映射查询结果的Map集合
Map<K,V> selectMap(String var1,Object var2,String var3)执行查询操作,需传入查询的方法和参数,返回Map集合
int insert(String var1)执行插入操作,需传入映射文件中的方法名,返回数据库中受影响的数据行数
int insert(String var1,Object var2)执行插入操作,需传入映射文件中的方法名和参数对象,返回数据库中受影响的数据行数
int update(String var1)执行更新操作,需传入映射文件中的方法名,返回数据库中受影响的数据行数
int update(String var1,Object var2)执行更新操作,需传入映射文件中的方法名和参数对象,返回数据库中受影响的数据行数
int delete(String var1)执行删除操作,需传入映射文件中的方法名,返回数据库中受影响的数据行数
int delete(String var1,Object var2)执行删除操作,需传入映射文件中的方法名和参数对象,返回数据库中受影响的数据行数
commit()提交事务
commit(boolean var1)var1默认为false,参数值为true时表示强制提交
rollback()回滚
rollback(boolean var1)强制回滚
close()关闭SqlSession对象
T getMapper(Class<T> var1)获取映射器

5. 配置日志:

有很多内容,了解即可

  • 导入log4j的依赖

    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.12</version>
    </dependency>
  • 在应用的类路径中创建一个名为 log4j.properties 的文件,文件的具体内容如下:

    # 全局日志配置
    log4j.rootLogger=ERROR, stdout
    # MyBatis 日志配置
    log4j.logger.org.mybatis.example.BlogMapper=TRACE
    # 控制台输出
    #用到的类
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    #打印的格式
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    #具体的格式指定
    log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

    日志记录器(Logger)的行为是分等级的:
    1.分为OFF、FATAL【致命错误】、ERROR、WARN、INFO【普通运行信息】、DEBUG【断点调试】、TRACE【跟踪信息】、ALL或者您定义的级别。

    2.Log4j建议只使用四个级别,优先级从高到低分别是ERROR、WARN、INFO、DEBUG。

    3.如果log level设置在某一个级别上,那么比此级别优先级高的log都能打印出来,就拿我们常用的4个等级ERROR、WARN、INFO、DEBUG,如果我们设置在INFO上,那程序中所有DEBUG级别的日志将不会打印。

    常用几个等级的说明:
    1、DEBUG 指定细粒度信息事件是最有用的应用程序调试,一般使用log.debug()进行跟踪调试。

    2、INFO 指定能够突出在粗粒度级别的应用程序运行情况的信息的消息,就是输出提示信息。info级别监控系统运行情况,可以帮助程序员有效的了解程序的流转。

    3、WARN 指定具有潜在危害的情况,一般很少使用。

    4、ERROR  错误事件可能仍然允许应用程序继续运行。就是显示错误信息。比如接口访问超时,用try/catch 捕获异常,发生异常的时候log.error输出错误信息,并不影响程序的运行。

  • 上述配置将使 Log4J 详细打印 org.mybatis.example.BlogMapper【详细的包】 的日志,对于应用的其它部分,只打印错误信息。

    为了实现更细粒度的日志输出,你也可以只打印特定语句的日志。以下配置将只打印语句 selectBlog 的日志:

    log4j.logger.org.mybatis.example.BlogMapper.selectBlog=TRACE
  • 或者,你也可以打印一组映射器的日志,只需要打开映射器所在的包的日志功能即可:

    log4j.logger.org.mybatis.example=TRACE

    某些查询可能会返回庞大的结果集。这时,你可能只想查看 SQL 语句,而忽略返回的结果集。为此,SQL 语句将会在 DEBUG 日志级别下记录(JDK 日志则为 FINE)。返回的结果集则会在 TRACE 日志级别下记录(JDK 日志则为 FINER)。因此,只要将日志级别调整为 DEBUG 即可:

    log4j.logger.org.mybatis.example=DEBUG

使用和指定语句的参数和返回值相匹配的接口(比如 UserMapper.class),现在你的代码不仅更清晰,更加类型安全,还不用担心可能出错的字符串字面值以及强制类型转换。

1.2 MyBatis接口开发(代理开发)【较为重要】

使用XML文件进行开发,在调用SqlSession进行操作时,需要指定MyBatis映射文件中的方法,这种调用方式过于烦琐。为解决此问题,MyBatis提供了接口开发的方式。

接口开发的目的:

  • 解决原生方式中的硬编码

  • 简化后期执行SQL

需修改的地方:

1.在mapper文件夹下创建XxxMapper接口,并定义相应的抽象方法。

2.在mapper文件夹下创建映射文件XxxMapper.xml,并指定其namespace为对应Mapper接口的绝对路径。

3.在MyBatis主配置文件中,将mapper包下所有的Mapper接口引入

<mappers>
    <!--<mapper resource="com/ambow/mapper/UserMapper.xml"/>-->
	<package name="com.foxbill.mapper">
<mappers>

4.在pom.xml中配置resource,指定打包资源,使mapper包中的映射文件可以被打包到classes中【另一种方式:也可以不做如下配置,而是把映射文件,放在resources对应的文件夹中】

    <build>
        <!--加入 resource 插件-->
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
            </resource>
        </resources>
    </build>

1. 实现步骤:

1.创建Mapper接口

package com.ambow.mapper;

import com.ambow.pojo.User;

import java.util.List;

public interface UserMapper {
    public List<User> selectUser();
    public List<User> searchUser(String keywords);
    public int insertUser(User user);
    public int updateUser(User user);
    public int deleteUser(int id);
}

2.修改mapper.xml文件中的namespace,放到mapper文件夹

<?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">
<!--MyBatis接口开发,需要修改namespace-->
<mapper namespace="com.ambow.mapper.UserMapper">
    <select id="selectUser" resultType="com.ambow.pojo.User">
        select * from tb_user
    </select>

    <select id="searchUser" resultType="com.ambow.pojo.User">
        select * from tb_user where username like '%${_parameter}%'
    </select>

    <!--新增用户-->
    <insert id="insertUser" parameterType="com.ambow.pojo.User">
        insert into tb_user values(null,#{username},#{password},#{gender},#{addr})
    </insert>

    <update id="updateUser" parameterType="com.ambow.pojo.User">
        update tb_user set username = #{username},password = #{password}, gender = #{gender},addr = #{addr}
        where id = #{id}
    </update>

    <delete id="deleteUser">
        delete from tb_user where id = #{id}
    </delete>

</mapper>

3. 修改主配置文件中映射文件的路径

<mappers>
    <!--<mapper resource="com/ambow/mapper/UserMapper.xml"/>-->
    <package name="com.ambow.mapper"/>
</mappers>

4.测试接口开发

    @Test
    public void test() throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //获取SqlSessionFactory - 工厂对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//        System.out.println(sqlSessionFactory);
        //获取SqlSession - 连接对象
        SqlSession sqlSession = sqlSessionFactory.openSession();

        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        List<User> list = userMapper.selectUser();
        for (User user : list) {
            System.out.println(user);
        }
      
    }

2. MyBatis动态代理原理

 

 

3. MyBatisX 插件

MybatisX 是一款基于 IDEA 的快速开发插件,为效率而生。

主要功能:

  • XML 和 接口方法 相互跳转

  • 根据接口方法生成 statement

安装:

 

 

1.3 MyBatis核心配置

0.核心配置文件概览

MyBatis配置文件中有MyBatis框架的核心配置,负责对MyBatis进行全局管理。它包含许多控制MyBatis功能的重要元素。

<configuration>
    <!--设置配置文件-->
    <properties>
        <property name="" value=""/>
    </properties>
    <!--MyBatis设置-->
    <settings>
        <setting name="" value=""/>
    </settings>
    <!--包名简化缩进-->
    <typeAliases>
        <typeAlias type=""/>
    </typeAliases>
    <!--配置数据类型转换-->
    <typeHandlers>
        <typeHandler handler=""/>
    </typeHandlers>
    <!--自定义结果集对象-->
    <objectFactory type="">
    </objectFactory>
    <!--配置插件-->
    <plugins>
        <plugin interceptor="">
        </plugin>
    </plugins>
       <!--配置环境-->
    <environments default="">
        <!--配置mysql环境-->
        <environment id="">
            <!--配置事务管理器-->
            <transactionManager type=""/>
            <!--配置数据库连接-->
            <dataSource type="">
                <!--配置数据库连接驱动-->
                <property name="" value=""/>
                <!--配置数据库连接地址-->
                <property name="" value=""/>
                <!--配置用户名-->
                <property name="" value=""/>
                <!--配置密码-->
                <property name="" value=""/>
            </dataSource>
        </environment>
    </environments>
    <!--数据厂商标识-->
    <databaseIdProvider type="">
       </databaseIdProvider>
    <!--配置mapper映射文件-->
    <mappers>
        <mapper 
           resource="com/mapper/DogMapper.xml"/>
    </mappers>
</configuration>

常见配置:

1.properties元素

第一种方式:

<properties>
        <property name="driver" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost:3306/jdbc" />
        <property name="username" value="root" />
        <property name="password" value="root" />
</properties>

第二种方式:

<properties resource="jdbc.properties" />

在resources文件夹里面配置配置文件,就可以就直接访问

对应的jdbc.properties源码如下:

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbc
username=root
password=root

属性值的引用方式为${属性名},具体代码参考如下:

    <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>
2.typeAliases
    <typeAliases>
        <!--给单个实体类配置别名-->
        <!--<typeAlias type="com.ambow.pojo.Student" alias="Student" />-->
        <!--给多个实体类配置别名,默认的别名就是类名【不区分大小写  Student、student】-->
        <package name="com.ambow.pojo" />
    </typeAliases>
3.mappers
    <mappers>
        <!-- 1.使用相对于类路径的资源引用 -->
        <!--<mapper resource="com/ambow/dao/StudentMapper.xml"/>-->
        <!-- 2.使用完全限定资源定位符(URL) -->
        <!--<mapper url="file:///E:\workspace\LearnMyBatis\mybatis03\src\main\java\com\ambow\dao\StudentMapper.xml" />-->
        <!-- 3.使用映射器接口实现类的完全限定类名 -->
        <!--<mapper class="com.ambow.dao.StudentMapper" />-->

        <!-- 4.将包内的映射器接口实现全部注册为映射器【推荐】 -->
        <package name="com.ambow.dao" />
    </mappers>
4.settings

了解一下即可,可以指定日志 

<settings>
    <setting name="logImpl" value="LOG4J"/>
</settings>

Mybatis 通过使用内置的日志工厂提供日志功能。内置日志工厂将会把日志工作委托给下面的实现之一:

  • SLF4J

  • Apache Commons Logging

  • Log4j 2

  • Log4j

  • JDK logging

MyBatis 内置日志工厂会基于运行时检测信息选择日志委托实现。它会(按上面罗列的顺序)使用第一个查找到的实现。当没有找到这些实现时,将会禁用日志功能。

如果你想选择某种日志实现,你可以通过上面的setting来指定。

1.4 MyBatis映射文件

映射文件是MyBatis中的重要组成部分,它包含了开发中编写的SQL语句、参数、结果集等。映射文件需要通过MyBatis配置文件中的<mapper>元素引入才能生效。MyBatis规定了映射文件的层次结构。

1.映射文件概述

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="MyDog">
    <!--开启此映射文件的缓存-->
    <cache/>
    <!--指定引用的命名空间,当此命名空间执行DML操作修改时,
    被引用的命名空间中的缓存也会失效-->
    <cache-ref namespace=""/>
    <!--参数映射集-->
    <parameterMap id="" type="">
        <parameter property="" jdbcType="" javaType="" typeHandler=""/>
    </parameterMap>
    <!--sql块-->
    <sql id=""></sql>
    <!--映射结果集-->
    <resultMap id="" type="">
        <id property="" column=""/>
        <result property="" column="" />
    </resultMap>
    <!--查询元素-->
    <select id="" resultType="" parameterType=""></select>
   <!--新增元素-->
    <insert id="" parameterType=""></insert>
    <!--删除元素-->
    <delete id=""></delete>
    <!--更新元素-->
    <update id="" parameterType=""></update>
</mapper>

< select >元素的常用属性:【作为了解内容,后续开发的时候直接查即可】

 

 

<sql>元素:

通过<sql>元素来包裹常用的列名,当需要使用此列名时,可以利用<include>元素的refid属性来指定。在需要修改列名时,通过修改<sql>元素即可实现全文件的列名修改。

<sql>元素设计的初衷在于简化列名的书写,在一个命名空间内,重复的列名将会给维护带来很大的麻烦,因此,MyBatis使用<sql>元素来包含这些列名,使其得到重用。

2.模糊查询总结

写法为:

1、使用“${...}”,语法为“like '${...}'”; -->and sName LIKE '%${sName}%'

<select id="query" resultType="student">
    SELECT * FROM students
    <where>
        <if test="null != sId and '' != sId">
            and sId = #{sId}
        </if>
        <if test="null != sName and '' != sName">
            and sName LIKE '%${sName}%'
        </if>
        <if test="null != sAge and '' != sAge">
            and sAge = #{sAge}
        </if>
    </where>
</select>

2、使用“#{...}”,语法为“like '#{...}'”; -->and sName LIKE "%"#{sName}"%"

<select id="query" resultType="student">
    SELECT * FROM students
    <where>
        <if test="null != sId and '' != sId">
            and sId = #{sId}
        </if>
        <if test="null != sName and '' != sName">
            and sName LIKE "%"#{sName}"%"
        </if>
        <if test="null != sAge and '' != sAge">
            and sAge = #{sAge}
        </if>
    </where>
</select>

3、使用[CONCAT]函数连接参数形式,语法为“like CONCAT('%',#{...},'%')”。-->and sName LIKE concat("%",#{sName},"%")

<select id="query" resultType="student">
    SELECT * FROM students
    <where>
        <if test="null != sId and '' != sId">
            and sId = #{sId}
        </if>
        <if test="null != sName and '' != sName">
            and sName LIKE concat("%",#{sName},"%")
        </if>
        <if test="null != sAge and '' != sAge">
            and sAge = #{sAge}
        </if>
    </where>
</select>

4、使用<bind> --><bind name="bindName" value="'%'+sName+'%'" />

    <select id="query" resultType="student">
        <!--命名元素-->
        <bind name="bindName" value="'%'+sName+'%'" />
        SELECT * FROM students
        <where>
            <if test="null != sId and '' != sId">
                and sId = #{sId}
            </if>
            <if test="null != sName and '' != sName">
                and sName LIKE #{bindName}
            </if>
            <if test="null != sAge and '' != sAge">
                and sAge = #{sAge}
            </if>
        </where>
    </select>

参考网址:

mybatis中LIKE模糊查询的几种写法以及注意点_mybatis 模糊查询_槐月十九的博客-CSDN博客

#{}和${}的区别:

“#{}”表示占位符,在组成SQL的过程中,先将此位置占位,之后将取得的值赋值到此位置,在类型上有严格的限制。【相当于用PreparedStatement,不会有SQL注入风险】

“${}”表示拼接符,在组成SQL的过程中,此符号将取得的值直接拼接到相应位置。【相当于用Statement,拼接SQL,有SQL注入风险】

1.5 MyBatis缓存

为了减少重复查询给数据库带来的压力,MyBatis提供了缓存机制,这种机制能够缓存查询的结果,避免重复的查询。

MyBatis提供了两种缓存方式,一种为针对于SqlSession的缓存,此种缓存方式默认开启;另一种为针对于全局的缓存,需要手动开启。一级缓存存在SqlSession对象中,二级缓存横跨全部的SqlSession,对所有的查询都生效。

 

 

1.一级缓存(本地缓存)【重点了解一级缓存】

在没有配置的情况下,MyBatis默认开启一级缓存。在实际开发时,使用同一个SqlSession对象调用同一个Mapper方法,往往只执行一次SQL,这是因为,当开启一级缓存时,第一次查询,MyBatis会将查询结果放在缓存中,当再次使用这个SqlSession进行同一个查询时,如果数据库的数据没有被更改,则直接将缓存中的数据返回,不会再次发送SQL到数据库

 

 

1.用户发送查询请求给MyBatis,MyBatis接收到请求时创建一个SqlSession对象处理本次请求的数据库操作,每个SqlSession对象有对应的执行器,执行器在执行SQL语句时会查询Local Cache中是否存在此查询的缓存,如果不存在,则执行此次查询,并将缓存放到Local Cache中;如果存在,则直接将此次查询的缓存返回。

2.当会话结束,即调用SqlSession的close()方法时,会释放此SqlSession中的所有缓存,并将此SqlSession禁用。如果想要清除缓存中的数据,而不关闭SqlSession对象,可以调用SqlSession的clearCache()方法,此方法会清空该SqlSession一级缓存中的所有内容。除此之外,当SqlSession中执行任何一个DML操作,即增加、删除或更改操作时,都将清空此SqlSession的一级缓存

在MyBatis中,对于两次查询,有以下四个条件来判定它们是否是完全相同的两次查询。 1)传入的statementId是否相同 2)查询时结果集范围是否相同 3)查询的最终SQL语句是否相同 4)传递给Statement的参数是否相同 当这些判断都相同时,认为这两次查询完全相同。

如果想要清除缓存中的数据,而不关闭SqlSession对象,可以调用SqlSession的clearCache()方法,此方法会清空该SqlSession一级缓存中的所有内容。除此之外,当SqlSession中执行任何一个DML操作,即增加、删除或更改操作时,都将清空此SqlSession的一级缓存

演示一级缓存的案例,实现步骤:

1. 添加MyBatis的依赖
    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <!-- mybatis的依赖 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.6</version>
        </dependency>
        <!-- mysql-connector-java 的依赖 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>
        <!-- lombok 的依赖-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
            <!--            <scope>provided</scope>-->
        </dependency>
        <!-- junit的依赖 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
            </resource>
        	<resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*</include>
                </includes>
            </resource>
        </resources>
    </build>
2.添加MyBatis的核心配置文件
<?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">
<!--MyBatis的核心配置文件-->
<configuration>
    <properties resource="jdbc.properties" />

    <settings>
        <!--开启数据库日志检测-->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

    <!--类型别名-->
    <typeAliases>
        <package name="com.ambow.pojo" />
    </typeAliases>

    <!--配置环境-->
    <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>
        <!-- 4.将包内的映射器接口实现全部注册为映射器【推荐】 -->
        <package name="com.ambow.dao" />
    </mappers>
</configuration>

核心配置文件,需要读取的jdbc.properties文件:

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbc
username=root
password=root
3.创建POJO - 基于Lombok
package com.ambow.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Dog {
    private int id;
    private String name;
    private int age;

}
4.创建Mapper接口
//DogMapper.java
package com.ambow.dao;

import com.ambow.pojo.Dog;

public interface DogMapper {
    Dog selectDog();
}
5.创建Mapper映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!--DogMapper.xml-->
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--MyBatis接口开发,需要修改namespace-->
<mapper namespace="com.ambow.dao.DogMapper">

    <select id="selectDog" resultType="dog">
        SELECT * from dog where id = 1
    </select>

</mapper>
6.创建MybatisUtil工具类
package com.ambow.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.InputStream;

public class MyBatisUtil {
    //获取SqlSession
    public static SqlSession getSqlSesssion(){
        //获取SqlSession
        String resource = "mybatis-config.xml";
        InputStream inputStream = null;
        try {
            inputStream = Resources.getResourceAsStream(resource);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //获取SqlSessionFactory - 工厂对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//        System.out.println(sqlSessionFactory);
        //获取SqlSession - 连接对象
        SqlSession sqlSession = sqlSessionFactory.openSession();
        return sqlSession;
    }

    //关闭SqlSession
    public static void closeSqlSession(SqlSession session){
        if (session != null) {
            session.close();
        }
    }

}
7.测试一级缓存
package com.ambow.test;

import com.ambow.dao.DogMapper;
import com.ambow.pojo.Dog;
import com.ambow.util.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

public class CacheTest {

    /*
        一级缓存:
            SqlSession级别的缓存,也就是说,同一个SqlSession共用一个缓存对象
     */
    @Test
    public void test01(){
        //获取SqlSession
        SqlSession sqlSesssion01 = MyBatisUtil.getSqlSesssion();

        //第一次查询 - id为1
        DogMapper dogMapper01 = sqlSesssion01.getMapper(DogMapper.class);
        Dog dog1 = dogMapper01.selectDog();
        System.out.println(dog1);

        //第二次查询 - id为1
        DogMapper dogMapper02 = sqlSesssion01.getMapper(DogMapper.class);
        Dog dog2 = dogMapper02.selectDog();
        System.out.println(dog2);
    }

    /*
        一级缓存:
            两个SqlSession对象,不会共用一个缓存对象
     */
    @Test
    public void test02(){
        //获取SqlSession
        SqlSession sqlSesssion01 = MyBatisUtil.getSqlSesssion();
        SqlSession sqlSesssion02 = MyBatisUtil.getSqlSesssion();

        //第一次查询 - id为1
        DogMapper dogMapper01 = sqlSesssion01.getMapper(DogMapper.class);
        Dog dog1 = dogMapper01.selectDog();
        System.out.println(dog1);

        //第二次查询 - id为1
        DogMapper dogMapper02 = sqlSesssion02.getMapper(DogMapper.class);
        Dog dog2 = dogMapper02.selectDog();
        System.out.println(dog2);
    }

    /*
        一级缓存:
            SqlSession调用close()方法,缓存会被释放
     */
    @Test
    public void test03(){
        //获取SqlSession
        SqlSession sqlSesssion01 = MyBatisUtil.getSqlSesssion();

        //第一次查询 - id为1
        DogMapper dogMapper01 = sqlSesssion01.getMapper(DogMapper.class);
        Dog dog1 = dogMapper01.selectDog();
        System.out.println(dog1);

        sqlSesssion01.close();

        //第二次查询 - id为1
        DogMapper dogMapper02 = sqlSesssion01.getMapper(DogMapper.class);
        Dog dog2 = dogMapper02.selectDog();
        System.out.println(dog2);
    }

    /*
        一级缓存:
            调用SqlSession的clearCache()方法,可以释放缓存
     */
    @Test
    public void test04(){
        //获取SqlSession
        SqlSession sqlSesssion01 = MyBatisUtil.getSqlSesssion();

        //第一次查询 - id为1
        DogMapper dogMapper01 = sqlSesssion01.getMapper(DogMapper.class);
        Dog dog1 = dogMapper01.selectDog();
        System.out.println(dog1);

        //清除缓存
        sqlSesssion01.clearCache();

        //第二次查询 - id为1
        DogMapper dogMapper02 = sqlSesssion01.getMapper(DogMapper.class);
        Dog dog2 = dogMapper02.selectDog();
        System.out.println(dog2);
    }

    /*
    一级缓存:
        当SqlSession中执行任何一个DML操作,即增加、删除或更改操作时,都将清空此SqlSession的一级缓存

 	*/
    @Test
    public void test05(){
        //获取SqlSession
        SqlSession sqlSesssion01 = MyBatisUtil.getSqlSesssion();

        //第一次查询 - id为1
        DogMapper dogMapper01 = sqlSesssion01.getMapper(DogMapper.class);
        Dog dog1 = dogMapper01.selectDog();
        System.out.println(dog1);

        //执行DML操作 - 数据更新
        int row = dogMapper01.updateDog();
        System.out.println("执行了更新语句");

        //第二次查询 - id为1
        DogMapper dogMapper02 = sqlSesssion01.getMapper(DogMapper.class);
        Dog dog2 = dogMapper02.selectDog();
        System.out.println(dog2);
    }
}

2.二级缓存(全局缓存)

MyBatis的二级缓存是Application级别的缓存,与一级缓存的原理类似,不同的是,二级缓存的作用域扩大到了每个命名空间,在同一个命名空间中的所有查询都将被缓存。

 

MyBatis二级缓存的执行流程:

1.MyBatis中的二级缓存默认关闭,需要手动开启,当开启后,用户发送有关数据库操作的请求会被CacheExecutor拦截。

2.CacheExecutor拦截数据库操作后,到Configuration对象中查看对应命名空间中的缓存,如果发现存在相同查询的缓存,则直接返回该缓存;如果不存在,则进入一级缓存中查找。即先经过二级缓存查找后,再从一级缓存中寻找。

 

MyBatis在执行到DML语句时,会清空当前命名空间中所有的缓存。此外,MyBatis开启二级缓存后可能会有脏读问题:按照开发规范,每个类都有自己的命名空间,命名空间不允许有针对其他类的更改,但如果在B类的命名空间中对A类做出更改时,B类命名空间中的二级缓存将会被清除,A类中的缓存不会被清除,当A类命名空间中有针对于A类的查询操作时,就会寻找二级缓存中的旧数据并将其返回。

演示案例

演示1:不开启二级缓存,一级缓存无法实现跨SqlSession之间的缓存。

演示2:开启二级缓存,可以实现跨SqlSession的缓存。

<!--设置 -->
    <settings>
        <!--缓存二级配置的全局开关-->
        <setting name="cacheEnabled" value="true" />
        <!--开启数据库日志检测-->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

使用MyBatis的二级缓存,需要以下几步:

  1. 在主配置文件中开启全局二级缓存配置

    <setting name="cacheEnabled" value="true">
  2. 在映射文件中加入<cache />标签

  3. 对应的pojo需要实现序列化

  4. 注意:测试二级缓存需要commit提交,如果不提交是不会保存到二级缓存的

演示3:执行DML操作后,二级缓存会清空。

演示4:在不规范开发时,二级缓存会出现脏读情况。

  • 按照开发规范,每个类都有自己的命名空间,命名空间不允许有针对其他类的更改,但如果在B类的命名空间中对A类做出更改时,B类命名空间中的二级缓存将会被清除,A类中的缓存不会被清除,当A类命名空间中有针对于A类的查询操作时,就会寻找二级缓存中的旧数据并将其返回。

面试题:说一说MyBatis的缓存机制?

首先MyBatis设计了二级缓存这样一个机制来提升数据检索效率,避免每一次检索都去查询数据库。

一级缓存是SqlSession级别的缓存,也叫本地缓存。因为每一个用户在执行查询的时候,都需要使用SqlSession来执行,为了避免每一次都去查询数据库,MyBatis把查询出来的数据缓存到SqlSession的本地缓存里面,后续的SQL查询,如果命中缓存的情况下,就可以直接从本地缓存去读取数据。

如果要实现跨SqlSession级别的缓存,那么一级缓存无法做到,因此MyBatis引入了二级缓存的设计。当多个用户查询数据的时候,只要有任何一个SqlSession拿到了数据,就会放到二级缓存里面,其他SqlSession就可以直接从二级缓存里面加载数据。

下面我再来解释一下一二级缓存的实现原理,首先看一级缓存,在SqlSession里面会持有一个Executor,每个Executor里面会有一个LocalCache的对象,当用户发起查询的时候,MyBatis会根据执行语句,在LocalCache里面去查找,如果命中了就只把数据返回,如果没有命中,再去数据库中查找,再写入到LocalCache里面,所以,一级缓存的生命周期是SqlSession.需要注意一点:在多个SqlSession或者分布式环境下,可能会因为一级缓存导致脏读的问题。

而二级缓存的实现原理呢,是在原来的Executor上去做了一个装饰,引入了叫CachingExecutor的装饰器,在进入一级缓存的查询之前,会先通过CachingExecutor进行二级缓存的查询。开启二级缓存之后,会被多个SqlSession共享,因此它是一个全局的缓存,所以它的查询流程就变成了,先去查询二级缓存,再去查询一级缓存,最后再去查数据库。另外,二级缓存相比一级缓存,实现了SqlSession之间的缓存数据的共享,同时缓存粒度可以控制到namespace级别,并且还可以通过Cache接口来实现不同缓存实现类的一个组合,对Cache的可控度也更高了。


注意:

第一版:MyBatisUtil.java

package com.ambow.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.InputStream;

public class MyBatisUtil {
    //获取SqlSession
    public static SqlSession getSqlSesssion(){
        //获取SqlSession
        String resource = "mybatis-config.xml";
        InputStream inputStream = null;
        try {
            inputStream = Resources.getResourceAsStream(resource);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //获取SqlSessionFactory - 工厂对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//        System.out.println(sqlSessionFactory);
        //获取SqlSession - 连接对象
        SqlSession sqlSession = sqlSessionFactory.openSession();
        return sqlSession;
    }

    //关闭SqlSession
    public static void closeSqlSession(SqlSession session){
        if (session != null) {
            session.close();
        }
    }

}

第二版:MyBatisUtil.java

package com.ambow.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.InputStream;

public class MyBatisUtil {

    private static SqlSessionFactoryBuilder builder;
    private static SqlSessionFactory sqlSessionFactory;

    //静态代码块 - 类加载的时候,只执行一次
    static {
        //获取SqlSession
        String resource = "mybatis-config.xml";
        InputStream inputStream = null;
        try {
            inputStream = Resources.getResourceAsStream(resource);
        } catch (IOException e) {
            e.printStackTrace();
        }
        builder = new SqlSessionFactoryBuilder();

        //获取SqlSessionFactory - 工厂对象
        sqlSessionFactory = builder.build(inputStream);
    }


    //获取SqlSession
    public static SqlSession getSqlSesssion(){
        //获取SqlSession - 连接对象
        SqlSession sqlSession = sqlSessionFactory.openSession();
        return sqlSession;
    }

    //关闭SqlSession
    public static void closeSqlSession(SqlSession session){
        if (session != null) {
            session.close();
        }
    }

    public static SqlSessionFactoryBuilder getBuilder(){
        return builder;
    }

    public static SqlSessionFactory getSqlSessionFactory(){
        return sqlSessionFactory;
    }

}

第二版才能共用一个SqlSessionFactory,而第一版拿到的是两个。

总结

以上就是今天的内容~

欢迎大家点赞👍,收藏⭐,转发🚀,
如有问题、建议,请您在评论区留言💬哦。

最后:转载请注明出处!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/59974.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CleanMyMac真的有必要买吗 2023年最新CleanMyMac和腾讯柠檬详细解析

在如今的电脑使用过程中&#xff0c;保持电脑干净整洁是一项重要的任务。而随着Mac电脑越来越受欢迎&#xff0c;Mac电脑清理软件也愈发流行。在众多的Mac电脑清理软件中&#xff0c;CleanMyMac是一款备受好评的软件。但是&#xff0c;很多人还在犹豫CleanMyMac有必要买吗&…

你真的了解什么是生成式AI吗?

最近正好有这样的机会&#xff0c;让我给一群非技术人士介绍生成式AI&#xff0c;忙忙碌碌了一阵子&#xff0c;结果发现受众还是未能理解什么是生成式AI&#xff0c;到底和之前的AI有什么区别。因此希望此篇能够帮助普通人真正理解生成式AI&#xff0c;有个直观印象。 人工智…

C语言---数据结构实验---哈夫曼树及哈夫曼编码的算法实现---图的基本操作

文章目录 写在前面哈夫曼树及哈夫曼编码的算法实现实验内容代码实现 图的基本操作实验内容代码实现 写在前面 本篇实验代码非本人写&#xff0c;代码源自外部&#xff0c;经调试解决了部分warning和error后在本地vs上可以正常运行&#xff0c;如有运行失败可换至vs 未来会重构…

Zebec Payroll :计划推2出 WageLink On-Demand Pay,进军薪酬发放领域

“Zebec Protocol生态旨以Web3的方式建立全新的公平秩序&#xff0c;基于其流支付体系构建的薪酬支付板块&#xff0c;就是解决问题的一把利刃” Zebec Protocol在创立之初就有着一个十分宏大的愿景&#xff0c;其希望通过Web3的方式来进一步打破世界上一些不公平现象。 Zebec …

【漏洞复现】Metabase 远程命令执行漏洞(CVE-2023-38646)

文章目录 前言声明一、漏洞介绍二、影响版本三、漏洞原理四、漏洞复现五、修复建议 前言 Metabase 0.46.6.1之前版本和Metabase Enterprise 1.46.6.1之前版本存在安全漏洞&#xff0c;未经身份认证的远程攻击者利用该漏洞可以在服务器上以运行 Metabase 服务器的权限执行任意命…

docker配置远程连接端口

配置docker 配置远程连接端口 vi /lib/systemd/system/docker.servicesystemctl daemon-reload && systemctl restart docker firewall-cmd --zonepublic --add-port2375/tcp --permanenthttp://node2:2375/version

Leetcode.931 下降路径最小和

题目链接 Leetcode.931 下降路径最小和 rating : 1573 题目描述 给你一个 n x n 的 方形 整数数组 m a t r i x matrix matrix &#xff0c;请你找出并返回通过 m a t r i x matrix matrix 的下降路径 的 最小和 。 下降路径 可以从第一行中的任何元素开始&#xff0c;并从…

机器学习基础知识(1)

什么是机器学习 机器学习是一种通过输入大量数据来构建一种模型&#xff08;网络&#xff09;&#xff0c;这个训练好的模型将会被用来预测或执行某些操作&#xff0c;这个训练的过程和方法就是机器学习。 我们也可以理解为构建一个“函数”&#xff0c;使得这个函数面对我们…

如何维护你的电脑:打造IT人的重要武器

文章目录 方向一&#xff1a;介绍我的电脑方向二&#xff1a;介绍我的日常维护措施1. 定期清理和优化2. 保持良好的上网习惯和安全防护3. 合理安排软件和硬件的使用4. 数据备份和系统还原 方向三&#xff1a;推荐的维护技巧1. 数据分区和多系统安装2. 内部清洁和散热优化3. 安全…

Html页面连线工具

在项目中遇了一个需要连线配置的功能。该功能引用了 bootstrap、layui 、svg-line等插件 下载链接 lixhttps://download.csdn.net/download/dongyan3595/88168121

docker 部署mysql 5.6集群

docker搭建mysql的集群&#xff08;一主双从&#xff09; 1.拉取镜像 docker pull mysql:5.6 2.启动master容器 docker run -it -d --name mysql_master -p 3306:3306 --ip 192.168.162.100 \ -v /data/mysql_master/mysql:/var/lib/mysql \ -v /data/mysql_master/conf.d…

【C++基础(六)】类和对象(中) --拷贝构造,运算符重载

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:C初阶之路⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习C   &#x1f51d;&#x1f51d; 类和对象 1. 前言2. 拷贝构造函数2.1 对拷贝构造函数…

5G网络在中国已经普及了,政策支持加大5G投入力度,这意味着什么呢?

5G网络是新型基础设施的重要组成部分&#xff0c;中国5G商用牌照已发放四年多&#xff0c;目前发展得怎样了&#xff1f;最近&#xff0c;官方公布了最新数据&#xff0c;截至7月底&#xff0c;中国5G移动电话用户已达7亿户&#xff0c;5G基站累计达到293.7万个&#xff0c;5G覆…

HDFS介绍

目录 ​编辑 一、HDFS基础 1.1 概述 1.2 HDFS的设计目标 1.2.1 硬件故障 1.2.2 流式数据访问 1.2.3 超大数据集 1.2.4 简单的一致性模型 1.2.5 移动计算而不是移动数据 1.2.6 跨异构硬件和软件平台的可移植性 1.3 基础概念 1.3.1 块&#xff08;Block&#xff09; 1.3.2 复制…

Nodejs 第四章(Npm install 原理)

在执行npm install 的时候发生了什么&#xff1f; 首先安装的依赖都会存放在根目录的node_modules,默认采用扁平化的方式安装&#xff0c;并且排序规则.bin第一个然后系列&#xff0c;再然后按照首字母排序abcd等&#xff0c;并且使用的算法是广度优先遍历&#xff0c;在遍历依…

STM32F0实现IAP升级固件

好几年前写过一篇关于 STM32 bootloader 升级固件的博客&#xff0c;但是使用的芯片是 STM32 F4 系列&#xff0c;升级固件的方式是在外部 flash 的 fat32 文件系统中存入固件文件&#xff0c;reset 后通过特定按键进入 IAP 程序。 最近需要在 STM32 上实现同样的 IAP 功能&am…

过程:从虚拟机上添加 git 并成功提交到 GitLab 的全过程

Ⅰ、准备工作&#xff1a; 1、Git 查看&#xff1a; 其一、命令&#xff1a;git --version // 此时就能在虚拟机环境下看到 git 的版本为: git version 2.41.0 其二、如何在虚拟机上安装 git &#xff1a; A、命令 &#xff1a; sudo apt-get install git B、然后再输入虚…

HDFS的QJM方案

Quorum Journal Manager仲裁日志管理器 介绍主备切换&#xff0c;脑裂问题解决---ZKFailoverController&#xff08;zkfc&#xff09;主备切换&#xff0c;脑裂问题解决-- Fencing&#xff08;隔离&#xff09;机制主备数据状态同步问题解决 HA集群搭建集群基础环境准备HA集群规…

宋浩概率论笔记(三)随机向量/二维随机变量

第三更&#xff1a;本章的内容最重要的在于概念的理解与抽象&#xff0c;二重积分通常情况下不会考得很难。此外&#xff0c;本次暂且忽略【二维连续型随机变量函数的分布】这一章节&#xff0c;非常抽象且难度较高&#xff0c;之后有时间再更新。

C++ 深拷贝和浅拷贝

虽然我们知道了拷贝构造函数&#xff0c;但是大多数简单程序中都不需要特别编写拷贝构造函数&#xff0c;隐含的拷贝构造函数足以实现对象间数据元素的一一对应复制。但是隐含的拷贝构造函数并不总是适用的&#xff0c;因为它完成的只是浅拷贝。 1.浅拷贝 【例】对象的浅拷贝…