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

news2024/12/22 17:03:49

系列文章目录

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,而第一版拿到的是两个。

总结

以上就是今天的内容~

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于Tars高并发IM系统的设计与实现-实战篇5

基于Tars高并发IM系统的设计与实现-实战篇5 群聊服务 GroupChatServer 群聊服务既可以接受来自BrokerServer的用户请求&#xff0c;也需要接收来自其他服务的RPC请求;所以本服务提供两套RPC接口&#xff1a;通用RPC接口和专用RPC接口。 通用RPC接口 通用RPC接口主要处理如下…

Jenkins自动化打包脚本

一、背景 jenkins可以设置定时任务打包&#xff0c;也已手动点按钮打包&#xff0c;还可以通过执行http请求打包&#xff0c;今天我们就通过shell脚本&#xff0c;通过curl命令进行jenkins打包。 二、步骤 2.1 在jenkins上构建项目 设置触发器 2.2 通过shell脚本触发远程构…

【RabbitMQ上手——单实例安装5种简单模式实现通讯过程】

【RabbitMQ入门-单实例安装&5种简单模式实现通讯过程】 一、环境说明二、安装RabbitMQ三、用户权限及Virtual Host设置四、5种简单模式实现通讯过程的实现五、小结 一、环境说明 安装环境&#xff1a;虚拟机VMWare Centos7.6 Maven3.6.3 JDK1.8RabbitMQ版本&#xff1a;…

并发——线程的生命周期和状态

文章目录 Java 线程在运行的生命周期中的指定时刻只可能处于下面 6 种不同状态的其中一个状态&#xff08;图源《Java 并发编程艺术》4.1.4 节&#xff09;。 线程在生命周期中并不是固定处于某一个状态而是随着代码的执行在不同状态之间切换。Java 线程状态变迁如下图所示&am…

点对点协议PPP

点对点协议PPP(Point-to-Point Protocol)是目前使用最广泛的点对点数据链路层协议。PPP协议是因特网的正确标准。 基本格式&#xff1a; PPP协议是数据链路格式。格式如下&#xff1a; 标志(Flag)字段: PPP的定界符&#xff0c;取值为0x7E 地址(Address)字段: 取值为0xFF&…

多语言自动翻译海外跨境电商独立站源码开发

要搭建一个多语言自动翻译的海外跨境电商独立站&#xff0c;需要进行以下步骤&#xff1a; 1. 选择合适的开发语言和框架&#xff1a;根据自己的技术实力和需求&#xff0c;选择适合的开发语言和框架。 2. 设计数据库结构&#xff1a;根据电商的业务需求&#xff0c;设计数据…

【CHI】架构介绍

Learn the architecture - Introducing AMBA CHI AMBA CHI协议导论--言身寸 1. AMBA CHI简介 一致性集线器接口&#xff08;CHI&#xff09;是AXI一致性扩展&#xff08;ACE&#xff09;协议的演进。它是Arm提供的高级微控制器总线架构&#xff08;AMBA&#xff09;的一部分。…

电源控制--对数与db分贝

在控制理论中&#xff0c;"db"通常表示分贝&#xff08;decibel&#xff09;的缩写。分贝是一种用于度量信号强度、增益或衰减的单位。 在控制系统中&#xff0c;分贝常用于描述信号的增益或衰减。通常&#xff0c;增益以正数的分贝值表示&#xff0c;而衰减以负数的…

C语言——九九乘法表

//九九乘法表 //用程序做一个九九乘法表 #include<stdio.h> int main() {int i,j,result;printf("\n");for(i1;i<10;i){for(j1;j<i;j){resulti*j;printf(" %d*%d%-d",i,j,result);}printf(" \n");}}

成集云 | 畅捷通采购单同步至钉钉 | 解决方案

源系统成集云目标系统 介绍 畅捷通是一家专业的金融科技公司&#xff0c;致力于为投资者提供便捷、高效的金融服务。通过畅捷通T的交易方式&#xff0c;投资者可以更加灵活地进行买卖交易&#xff0c;并且在交易完成后即可获得结算款项&#xff0c;无需等待T1的结算周期。 钉…

利用multiprocessing实现多线程,并实现多个参数传递函数的多并行

前言 利用多线程一般来说都是有 一定的大数据需求。 比如一个函数可能被不断的调用很多次 一般来说我们会使用for循环&#xff0c;但是为了节省时间&#xff0c;我们采用多线程的方式来解决这个问题 show you code 单参数输入 举了两个例子&#xff0c;一看便知 func为我们的函…

探索MongoDB的奥秘:基本命令使用入门指南

&#x1f60a; 作者&#xff1a; 一恍过去 &#x1f496; 主页&#xff1a; https://blog.csdn.net/zhuocailing3390 &#x1f38a; 社区&#xff1a; Java技术栈交流 &#x1f389; 主题&#xff1a; 探索MongoDB的奥秘&#xff1a;基本命令使用入门指南 ⏱️ 创作时间&a…

世界算力简史(下)

世界算力简史&#xff08;上&#xff09; 世界算力简史&#xff08;中&#xff09; 今天终于要完结了…… █ 1980-1990&#xff1a;PC时代 IBM-PC和“兼容机” 上一篇&#xff0c;我们说到&#xff0c;70年代微处理器崛起&#xff0c;使得个人电脑开始大量出现。 这种情况&…

山东布谷科技直播程序源码使用Redis进行服务器横向扩展

当今&#xff0c;直播程序源码平台作为新媒体时代主流&#xff0c;受到了世界各地人民的喜爱&#xff0c;这也使得直播程序源码平台用户数量的庞大&#xff0c;也难免会出现大量用户同时访问服务器&#xff0c;使服务器过载的情况&#xff0c;当服务器承受不住的时候&#xff0…

进程的创建

进程创建时发生了什么 回顾上节关于存储空间分配的图片&#xff1a; 当程序运行到 fork() 函数了之后&#xff1a; 在早期的Linux中&#xff0c;系统会将fork之前所有的数据段&#xff0c;代码段&#xff0c;堆&#xff0c;栈等对应的全部的存储空间拷贝一份&#xff0c;作为…

保姆级教程:从0到1搭建Stable Diffusion XL完整工作流进行AI绘画

Rocky Ding 公众号&#xff1a;WeThinkIn 写在前面 【人人都是算法专家】栏目专注于分享Rocky在AI行业中对业务/竞赛/研究/产品维度的思考与感悟。欢迎大家一起交流学习&#x1f4aa; 大家好&#xff0c;我是Rocky。 之前Rocky详细介绍了Stable Diffusion&#xff08;SD&#…

Scala(第一章Scala入门)

文章目录 1.1 概述 1.1.1 为什么学习Scala1.1.2 Scala发展历史1.1.3 Scala和Java关系1.1.4 Scala语言特点 1.2 Scala环境搭建1.3 Scala插件安装1.4 HelloWorld案例 1.4.1 创建IDEA项目工程1.4.2 class和object说明1.4.3 Scala程序反编译 1.5 关联Scala源码1.6官方编程指南 1.1…

Arch Linux 使用桥接模式上网

如果我们想要将虚拟机与物理主机同一网段&#xff0c;并且像物理机器一样被其他设备访问&#xff0c;则需要以桥接模式上网&#xff0c;这个时候&#xff0c;物理主机就必须配置为使用网桥上网了。 注意&#xff1a;这里我们使用了 NetworkManager 网络管理工具中的 nmcli 来进…

File 类和 InputStream, OutputStream 的用法总结

目录 一、File 类 1.File类属性 2.构造方法 3.普通方法 二、InputStream 1.方法 2.FileInputStream 三、OutputStream 1.方法 2.FileOutputStream 四、针对字符流对象进行读写操作 一、File 类 1.File类属性 修饰符及类型属性说明static StringpathSeparator依赖于系统的路…

【T3】金蝶kis凭证数据转换到畅捷通T3软件中。

【问题需求】 将金蝶软件中的账套转换到畅捷通T3软件中。 由于金蝶老版本使用的是非sql server数据库。 进而需要将其数据导入到sql中,在转换到T3。 【转换环境】 金蝶中数据:凭证;科目无项目核算。 1、金蝶的数据文件后缀为.AIS; 2、安装office2003全版软件; 3、安装sq…