【精选必看】MyBatis映射文件及动态SQL,一级,二级缓存介绍

文章目录

MyBatis映射文件

在这里插入图片描述

< r e s u l t M a p > <resultMap> <resultMap>

MyBatis映射文件中除了<insert><delete><update><select>外,还有一些标签可以使用:

resultMap

标签的作用的自定义映射关系。

MyBatis可以将数据库结果集封装到对象中,是因为结果集的列名和对象属性名相同:

当POJO属性名和数据库列名不一致时,MyBatis无法自动完成映射关系。

此时有两种解决方案:

  1. Sql语句的查询字段起与POJO属性相同的别名。

    <select id="findAll" resultType="com.mybatis.pojo.Teacher">
       select tid as id,tname as teacherName from teacher;
    </select>
    
  2. 自定义映射关系

    • 在映射文件中,使用<resultMap>自定义映射关系:
    <!-- id:自定义映射名 type:自定义映射的对象类型  -->
    <resultMap id="teacherMapper" type="com.mybatis.pojo.Teacher">
      <!-- id定义主键列  property:POJO属性名 column:数据库列名  -->
      <id property="id" column="tid"></id>
      <!-- result定义普通列  property:POJO属性名 column:数据库列名  -->
      <result property="teacherName" column="tname"></result>
    </resultMap>
    
    • <select>标签中,使用resultMap属性代替resultType属性,使用自定义映射关系。
    <select id="findAll" resultMap="teacherMapper">
       select * from teacher
    </select>
    

< sql>&< include>

<sql>用来定义可重用的Sql片段,通过<include>引入该片段。如:Sql语句的查询字段起与POJO属性相同的别名,该Sql片段就可以重用。

<sql id="selectAllField">
   select tid as id,tname as teacherName
</sql>


<select id="findAll" resultType="com.mybatis.pojo.Teacher">
  <include refid="selectAllField"></include>
   from teacher;
</select>


<select id="findById" resultType="com.mybatis.pojo.Teacher">
  <include refid="selectAllField"></include>
   from teacher where tid = #{id}
</select>

特殊字符处理

在Mybatis映射文件中尽量不要使用一些特殊字符,如:<>等。

我们可以使用符号的实体来表示:(带后面的分号)

符号实体
<& lt;
>& gt;
&& amp;
& apos;
& quot;

如:

<select id="findById2" resultType="com.mybatis.pojo.Teacher">
  <include refid="selectAllField"></include>
   from teacher where tid &gt; #{id}
</select>

动态SQL

在这里插入图片描述

一个查询的方法的Sql语句不一定是固定的。比如电商网站的查询商品,用户使用不同条件查询,Sql语句就会添加不同的查询条件。此时就需要在方法中使用动态Sql语句。

< i f > < if> <if>

<if>标签内的Sql片段在满足条件后才会添加,用法为:<if test="条件">。例如:根据不同条件查询用户:

  1. 持久层接口添加方法

    // 用户通用查询
    List<User> findByCondition(User user);
    
  2. 映射文件添加标签

    <select id="findByCondition" parameterType="com.mybatis.pojo.User" resultType="com.mybatis.pojo.User">
       select * from user where 1 = 1
      <if test="username != null and username.length() != 0">
         and username like #{username}
      </if>
      <if test="sex != null and sex.length() != 0">
         and sex = #{sex}
      </if>
      <if test="address != null and address.length() != 0">
         and address = #{address}
      </if>
    </select>
    
  3. 编写测试方法

    @Test
    public void testFindByCondition(){
      User user = new User();
      List<User> users1 = userMapper2.findByCondition(user);
      //users1.forEach(System.out::println);
    
    
      user.setUsername("%张三%");
      List<User> users2 = userMapper2.findByCondition(user);
      users2.forEach(System.out::println);
    
    
      user.setAddress("北京");
      List<User> users3 = userMapper2.findByCondition(user);
      users3.forEach(System.out::println);
    }
    
  1. if中的条件不能使用&&/||,而应该使用and/or

  2. if中的条件可以直接通过属性名获取参数POJO的属性值,并且该值可以调用方法。

  3. where后为什么要加1=1?

    任意条件都可能拼接到Sql中。如果有多个条件,从第二个条件开始前都需要加And关键字。加上1=1这个永久成立的条件,就不需要考虑后面的条件哪个是第一个条件,后面的条件前都加And关键字即可。

< w h e r e > <where> <where>

<where>可以代替sql中的where 1=1 和第一个and,更符合程序员的开发习惯,使用<where>后的映射文件如下:

<select id="findByCondition" resultType="com.mybatis.user.User" parameterType="com.mybatis.user.User">
   select * from user
  <where>
    <if test="username != null and username.length() != 0">
       username like #{username}
    </if>
    <if test="sex != null and sex.length() != 0">
       and sex = #{sex}
    </if>
  </where>
</select>

< s e t > <set> <set>

<set>标签用在update语句中。借助<if>,可以只对有具体值的字段进行更新。<set>会自动添加set关键字,并去掉最后一个if语句中多余的逗号。

<update id="update" parameterType="com.mybatis.user.User">
   update user
  <set>
    <if test="username != null and username.length() > 0">
       username = #{username},
    </if>
    <if test="sex != null and sex.length() > 0">
       sex = #{sex},
    </if>
  </set>
  <where>
     id = #{id}
  </where>
</update>

< c h o o s e > < choose> <choose> < w h e n > < when> <when> < o t h e r w i s e > < otherwise> <otherwise>

这些标签表示多条件分支,类似JAVA中的switch...case<choose>类似switch<when>类似case<otherwise>类似default,用法如下:

<select id="findByCondition" resultType="com.mybatis.user.User" parameterType="com.mybatis.user.User">
   select * from user
  <where>
    <choose>
      <when test="username.length() &lt; 5">
         username like #{username}
      </when>
      <when test="username.length() &lt; 10">
         username = #{username}
      </when>
      <otherwise>
         id = 1
      </otherwise>
    </choose>
  </where>
</select>

这段代码的含义为:用户名<5时使用模糊查询,用户名>=5并且<10时使用精确查询,否则查询id为1的用户

< f o r e a c h > <foreach> <foreach>

<foreach>类似JAVA中的for循环,可以遍历集合或数组。<foreach>有如下属性:

  • collection:遍历的对象类型
  • open:开始的sql语句
  • close:结束的sql语句
  • separator:遍历每项间的分隔符
  • item:表示本次遍历获取的元素,遍历List、Set、数组时表示每项元素,遍历map时表示键值对的值。
  • index:遍历List、数组时表示遍历的索引,遍历map时表示键值对的键。
遍历数组

我们使用<foreach>遍历数组进行批量删除。

  1. 持久层接口添加方法

    void deleteBatch(int[] ids);
    
  2. 映射文件添加标签

    <delete id="deleteBatch" parameterType="int">
       delete from user
      <where>
        <foreach open="id in(" close=")" separator="," collection="array" item="id" >
           #{id}
        </foreach>
      </where>
    </delete>
    
  3. 编写测试方法

    @Test
    public void testDeleteBatch(){
      int[] ids = {9,11};
      userMapper.deleteBatch(ids);
      session.commit();
    }
    
遍历Collection

<foreach>遍历List和Set的方法是一样的,我们使用<foreach>遍历List进行批量添加。

  1. 持久层接口添加方法

    void insertBatch(List<User> users);
    
  2. 映射文件添加标签

    <insert id="insertBatch" parameterType="com.mybatis.user.User">
       insert into user values
      <foreach collection="list" item="user" separator=",">
         (null ,#{user.username},#{user.sex},#{user.address})
      </foreach>
    </insert>
    
  3. 编写测试方法

    @Test
    public void testInsertBatch(){
      User user1 = new User("程序员1", "男", "北京");
      User user2 = new User("程序员2", "女", "上海");
      List<User> users = new ArrayList();
      users.add(user1);
      users.add(user2);
    
    
      userMapper2.insertBatch(users);
      session.commit();
    }
    
遍历Map

我们使用<foreach>遍历Map进行多条件查询。

  1. 持久层接口添加方法

    /**
       * 多条件查询
       * @param map 查询的条件键值对 键:属性名 值:属性值
       * @return
       */
    List<User> findUser(@Param("queryMap") Map<String,Object> map);
    
  2. 映射文件添加标签

    <select id="findUser" parameterType="map" resultType="com.mybatis.pojo.User">
       select * from user
      <where>
        <foreach collection="queryMap" separator="and" index="key" item="value">
           ${key} = #{value}
        </foreach>
      </where>
    </select>
    
  3. 编写测试方法

    @Test
    public void testFindUser(){
      Map<String,Object> queryMap = new HashMap();
      queryMap.put("sex","男");
      queryMap.put("address","北京");
      List<User> users = userMapper2.findUser(queryMap);
      users.forEach(System.out::println);
    }
    

MyBatis缓存

在这里插入图片描述

缓存介绍

缓存是内存当中一块存储数据的区域,目的是提高查询效率。MyBatis会将查询结果存储在缓存当中,当下次执行相同的SQL时不访问数据库,而是直接从缓存中获取结果,从而减少服务器的压力。

  • 什么是缓存?

    存在于内存中的一块数据。

  • 缓存有什么作用?

    减少程序和数据库的交互,提高查询效率,降低服务器和数据库的压力。

  • 什么样的数据使用缓存?

    经常查询但不常改变的,改变后对结果影响不大的数据。

  • MyBatis缓存分为哪几类?

    一级缓存和二级缓存

  • 如何判断两次Sql是相同的?

    1. 查询的Sql语句相同
    2. 传递的参数值相同
    3. 对结果集的要求相同
    4. 预编译的模板Id相同

MyBatis一级缓存

  • MyBatis一级缓存也叫本地缓存。SqlSession对象中包含一个Executor对象,Executor对象中包含一个PerpetualCache对象,在该对象存放一级缓存数据。
  • 由于一级缓存是在SqlSession对象中,所以只有使用同一个SqlSession对象操作数据库时才能共享一级缓存
  • MyBatis的一级缓存是默认开启的,不需要任何的配置。
测试一级缓存
@Test
public void testCache1() throws IOException {
  InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
  SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
  SqlSessionFactory factory = builder.build(is);
  SqlSession session = factory.openSession();


  // 使用同一个SqlSession查询
  UserMapper mapper1 = session.getMapper(UserMapper.class);
  UserMapper mapper2 = session.getMapper(UserMapper.class);


  User user1 = mapper1.findById(1);
  System.out.println(user1.hashCode());
  System.out.println("-------------------------------------------");
  User user2 = mapper2.findById(1);
  System.out.println(user2.hashCode());
}


@Test
public void testCache2() throws IOException {
  InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
  SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
  SqlSessionFactory factory = builder.build(is);
  SqlSession session1 = factory.openSession();
  SqlSession session2 = factory.openSession();


  // 使用不同的SqlSession查询
  UserMapper mapper1 = session1.getMapper(UserMapper.class);
  UserMapper mapper2 = session2.getMapper(UserMapper.class);


  User user1 = mapper1.findById(1);
  System.out.println(user1.hashCode());
  System.out.println("-------------------------------------------");
  User user2 = mapper2.findById(1);
  System.out.println(user2.hashCode());
}

MyBatis清空一级缓存

进行以下操作可以清空MyBatis一级缓存:

  1. SqlSession调用close():操作后SqlSession对象不可用,该对象的缓存数据也不可用。
  2. SqlSession调用clearCache()/commit():操作会清空一级缓存数据。
  3. SqlSession调用增删改方法:操作会清空一级缓存数据,因为增删改后数据库发生改变,缓存数据将不准确。
@Test
public void testCache3() throws IOException {
  InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
  SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
  SqlSessionFactory factory = builder.build(is);
  SqlSession session = factory.openSession();


  UserMapper mapper1 = session.getMapper(UserMapper.class);
  UserMapper mapper2 = session.getMapper(UserMapper.class);


  User user1 = mapper1.findById(1);
  System.out.println(user1.hashCode());
  //     session.close();
  //     session.clearCache();
  //     session.commit();
  mapper1.delete(2);
  System.out.println("-------------------------------------------");
  User user2 = mapper2.findById(1);
  System.out.println(user2.hashCode());
}

MyBatis二级缓存

  • MyBatis二级缓存也叫全局缓存。数据存放在SqlSessionFactory中,只要是同一个工厂对象创建的SqlSession,在进行查询时都能共享数据。一般在项目中只有一个SqlSessionFactory对象,所以二级缓存的数据是全项目共享的。

  • MyBatis一级缓存存放的是对象,二级缓存存放的是对象的数据。所以要求二级缓存存放的POJO必须是可序列化的,也就是要实现Serializable接口。

  • MyBatis二级缓存默认不开启,手动开启后数据先存放在一级缓存中,只有一级缓存数据清空后,数据才会存到二级缓存中。

    SqlSession调用clearCache()无法将数据存到二级缓存中。

开启二级缓存
  1. POJO类实现Serializable接口。

    public class User implements Serializable {
      private int id;
      private String username;
      private String sex;
      private String address;
    }
    
    
  2. 在MyBatis配置文件添加如下设置:

    <settings>
      <setting name="cacheEnabled" value="true"/>
    </settings>
    
    

    由于cacheEnabled默认值是true,所以该设置可以省略。

  3. 在映射文件添加<cache />标签,该映射文件下的所有方法都支持二级缓存。

    如果查询到的集合中对象过多,二级缓存只能缓存1024个对象引用。可以通过<cache />标签的size属性修改该数量。

    <cache size="2048"/>
    
  4. 测试二级缓存

    @Test
    public void testCache4() throws IOException {
      InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
      SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
      SqlSessionFactory factory = builder.build(is);
      SqlSession session1 = factory.openSession();
      SqlSession session2 = factory.openSession();
    
    
      UserMapper mapper1 = session1.getMapper(UserMapper.class);
      UserMapper mapper2 = session2.getMapper(UserMapper.class);
    
    
      User user1 = mapper1.findById(1);
      System.out.println(user1);
      System.out.println(user1.hashCode());
      // 让一级缓存失效
      session1.commit();
      System.out.println("-------------------------------------------");
    
    
      User user2 = mapper2.findById(1);
      System.out.println(user2);
      System.out.println(user2.hashCode());
    }
    
    

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

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

相关文章

Redis分片集群

文章目录 Redis分片集群搭建分片集群散列插槽插槽原理小结 集群伸缩需求分析创建新的redis实例添加新节点到redis转移插槽 故障转移自动故障转移手动故障转移 RedisTemplate访问分片集群 Redis分片集群 搭建分片集群 主从和哨兵可以解决高可用、高并发读的问题。但是依然有两…

大数据数据仓库,Sqoop--学习笔记

数据仓库介绍 1. 数据仓库概念 数据仓库概念创始人在《建立数据仓库》一书中对数据仓库的定义是&#xff1a;数据仓库&#xff08;Data Warehouse&#xff09;是一个面向主题的&#xff08;Subject Oriented&#xff09;、数据集成的&#xff08;Integrated&#xff09;、相对…

【C++】String类

目录 本文将对string常用函数进行说明 string基础介绍 string类的常用接口说明 string() &&string(const char* s) 拷贝:string (const string& str, size_t pos, size_t len npos) 拷贝前n个进行构造&#xff1a;string (const char* s, size_t n);​编辑 …

快速幂算法详解(C++实现)

文章目录 1. 什么是快速幂2. 暴力求解代码实现缺陷分析 3. 优化一&#xff1a;取模运算的性质4. 优化二&#xff1a;快速幂算法的核心思想5. 终极优化&#xff1a;位运算优化6. 源码 这篇文章我们来一起学习一个算法——快速幂算法。 1. 什么是快速幂 顾名思义&#xff0c;快速…

将本地项目上传到gitee

本文详细介绍如何将本地项目上传到gitee 1.登录gitee创建一个与本地项目名相同的仓库 2.进入本地项目所在路径&#xff0c;打开Git Bash 3.执行初始化命令 git init4.添加远程仓库 4.1 点击复制你的HTTPS仓库路径 4.2 执行添加远程仓库命令 git remote add origin 你的…

【Vue】filter的用法

上一篇&#xff1a; vue的指令 https://blog.csdn.net/m0_67930426/article/details/134599378?spm1001.2014.3001.5502 本篇所使用指令 v-for v-on v-html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"&…

vivado产生报告阅读分析23-时序路径特性报告

时序路径特性报告 下图显示了在“ Timing Mode ” &#xff08; 时序模式 &#xff09; 下运行“ Report Design Analysis ” &#xff08; 设计分析报告 &#xff09; 的输出示例 &#xff0c; 其中显示了设计中 10 条最差建立路径的路径特性。在 Vivado IDE 中选中“ Repo…

【教学类-06-12】20231126 (一)二位数 如何让加减乘除题目从小到大排序(以1-20之间加法为例,做正序排列用)

结果展示 优化后 优化前 背景需求&#xff1a; 生成列表 单独抽取显示题目排序方法 存在问题: 我希望 00 01 02……这样排序&#xff0c;但是实际上&#xff0c;除了第一个加数会从小到大排序&#xff0c;第二个被加数的第十位数和个位数都会从小到大排序&#xff0c;也就是…

Blender学习--模型贴图傻瓜级教程

Blender 官方文档 1. Blender快捷键&#xff1a; 快捷键说明 按住鼠标滚轮&#xff1a;移动视角Tab&#xff1a;切换编辑模式和物体模式鼠标右键&#xff1a; 编辑模式&#xff1a; 物体模式&#xff1a; 其他&#xff1a; 2. 下面做一个球体贴一张纹理的操作 2.1 效果如下…

智能优化算法应用:基于粒子群算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于粒子群算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于粒子群算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.粒子群算法4.实验参数设定5.算法结果6.参考文献7.…

C++局域网从服务器获取已连接用户的列表(linux to linux)

目录 服务器端 代码 客户端 代码解析 服务器端 原理 遇到的阻碍以及解决办法 客户端 原理 遇到的阻碍以及解决办法 运行结果截图 总结 服务器端 代码 #include <sys/types.h> #include <sys/socket.h> #include <stdio.h> #include <netinet…

安捷伦E4404B频谱分析仪,100 Hz 至 6.7 GHz

E4404B是安捷伦ESA-E系列频谱分析仪&#xff0c;它是一款能够适应未来发展需求的中高端频谱分析仪解决方案。该系列在频谱分析仪的测量速度、动态范围、精度和功率分辨能力等方面&#xff0c;都为类似价位的产品树立了性能标杆。其灵活的平台设计使得研发、制造和现场服务工程师…

这一款 Mac 系统终端工具,已经用的爱不释手了!

&#x1f525;&#x1f525;&#x1f525;作为程序员或者运维管理人员&#xff0c;我们经常需要使用终端工具来进行服务器管理及各种操作&#xff0c;比如部署项目、调试代码、查看/优化服务、管理服务器等。 相信大家用的最多的终端工具就是 Xshell、iTerm2和Mobaxterm&#…

利用ngrok实现内网穿透(全网最详细教程)

准备工具&#xff1a; 1、phpstudy 用于在本地搭建网站 2、ngrok 用于将自己的本地端口暴露到公网上&#xff0c;从而实现内网穿透 文章开始前给大家分享一个学习人工智能的网站&#xff0c;通俗易懂&#xff0c;风趣幽默 人工智能https://www.captainbed.cn/myon/ ~~~~~…

ESP32和ESP8266的ESP-MESH

ESP32和ESP8266的ESP-MESH 功能介绍一、介绍ESP-MESH二、安装painlessMesh库三、ESP-MESH基本示例&#xff08;广播消息&#xff09;四、示范 功能介绍 了解如何使用ESP-MESH网络协议通过ESP32和ESP8266 NodeMCU板构建网状网络。 ESP-MESH允许多个设备&#xff08;节点&#x…

单例模式与多线程

目录 前言 正文 1.立即加载/饿汉模式 2.延迟加载/懒汉模式 1.延迟加载/懒汉模式解析 2.延迟加载/懒汉模式的缺点 3.延迟加载/懒汉模式的解决方案 &#xff08;1&#xff09;声明 synchronized 关键字 &#xff08;2&#xff09;尝试同步代码块 &#xff08;3&am…

vue 中 js 金额数字转中文

参考&#xff1a;js工具函数之数字转为中文数字和大写金额_js封装工具类函数金额大写-CSDN博客 我使用的框架vol.core。 客户需求要将录入框的金额数字转换成中文在旁边显示&#xff0c;换了几种函数&#xff0c;最终确定如下函数 function changeToChineseMoney(Num) {//判断…

Quartz定时任务基础

springBoot有一个定时执行某个方法的 注解&#xff1a; Scheduled 可以满足挺多的需求&#xff0c;但是到了一些场景&#xff0c;就显得比较麻烦&#xff0c;比如&#xff1a; 机器待机五分钟后执行切换待机状态。如果是按照使用Scheduled注解&#xff0c;就得持久化一个表&…

【Java SE】 带你走近Java的抽象类与接口

&#x1f339;&#x1f339;&#x1f339;【JavaSE】专栏&#x1f339;&#x1f339;&#x1f339; &#x1f339;&#x1f339;&#x1f339;个人主页&#x1f339;&#x1f339;&#x1f339; &#x1f339;&#x1f339;&#x1f339;上一篇文章&#x1f339;&#x1f339;&…

2018年3月26日 Go生态洞察:Go包版本管理提案分析

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…