深入掌握MyBatis:连接池、动态SQL、多表查询与缓存

文章目录

  • 一、MyBatis连接池
    • 1.1 连接池的作用
      • 1.2 MyBatis连接池分类
  • 二、动态SQL
    • 2.1 if标签
    • 2.2 where标签
    • 2.3 foreach标签
    • 2.4 SQL片段复用
  • 三、多表查询
      • 3.1 多对一查询(一对一)
      • 3.2 一对多查询
  • 四、延迟加载
      • 4.1 立即加载 vs 延迟加载
      • 4.2 配置延迟加载
  • 五、MyBatis缓存
    • 5.1 一级缓存
    • 5.2 二级缓存
    • 5.3 一级缓存 vs 二级缓存:核心差异
    • 5.4 缓存使用陷阱与最佳实践**
  • 总结

一、MyBatis连接池

1.1 连接池的作用

  • 什么是连接池:存储数据库连接的容器,避免频繁创建和销毁连接。
  • 解决的问题:每次执行SQL都创建连接会浪费资源,连接池复用连接提升性能。

1.2 MyBatis连接池分类

类型描述
POOLED使用连接池(默认)
UNPOOLED不使用连接池(适合简单场景)
JNDI通过JNDI获取外部连接池

配置示例

<dataSource type="POOLED"><property name="driver" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/test"/><property name="username" value="root"/>
</dataSource>

二、动态SQL

2.1 if标签

动态拼接查询条件,避免手动拼接SQL字符串。

<select id="findByWhere" resultType="User">SELECT * FROM user<where><if test="username != null">AND username LIKE #{username}</if><if test="sex != null">AND sex = #{sex}</if></where>
</select>

2.2 where标签

自动处理WHERE后的AND/OR冗余,替代WHERE 1=1

<where><if test="username != null">username LIKE #{username}</if>
</where>

2.3 foreach标签

遍历集合生成条件,支持INOR拼接。

<!-- IN (1,2,3) -->
<foreach collection="ids" item="i" open="id IN (" separator="," close=")">#{i}
</foreach><!-- OR id=1 -->
<foreach collection="ids" item="i" open="id=" separator=" OR id=">#{i}
</foreach>

2.4 SQL片段复用

提取公共SQL片段,减少重复代码。

<sql id="baseSelect">SELECT * FROM user</sql>
<select id="findAll"><include refid="baseSelect"/>
</select>

三、多表查询

3.1 多对一查询(一对一)

需求:查询账户信息并关联用户信息。

JavaBean

public class Account {private Integer id;private Double money;private User user; // 关联用户
}

XML配置

<resultMap id="accountMap" type="Account"><id property="id" column="id"/><association property="user" javaType="User"><result property="username" column="username"/></association>
</resultMap><select id="findAll" resultMap="accountMap">SELECT a.*, u.username FROM account a JOIN user u ON a.uid = u.id
</select>

3.2 一对多查询

需求:查询用户及其所有账户。

JavaBean

public class User {private List<Account> accounts; // 用户拥有多个账户
}

XML配置

<resultMap id="userMap" type="User"><collection property="accounts" ofType="Account"><result property="money" column="money"/></collection>
</resultMap><select id="findOneToMany" resultMap="userMap">SELECT u.*, a.money FROM user u LEFT JOIN account a ON u.id = a.uid
</select>

四、延迟加载

延迟加载,也叫懒加载,简单来说就是在真正需要数据的时候才去加载数据,而不是在一开始就把所有关联数据都加载出来。这可以有效减少资源的浪费,尤其是在处理复杂对象关系时,避免一次性加载大量无用数据。

4.1 立即加载 vs 延迟加载

  • 立即加载:查询主对象时,直接加载关联对象(默认)。
  • 延迟加载:按需加载关联对象,提升性能。

4.2 配置延迟加载

开启全局延迟加载

<settings><setting name="lazyLoadingEnabled" value="true"/><setting name="aggressiveLazyLoading" value="false"/>
</settings>

一对一延迟加载示例
假设有 UserAddress 两个实体,一个用户对应一个地址。在传统的查询方式中,可能会一次性将用户及其地址信息都查询出来。但在延迟加载模式下,查询用户时并不会立即查询地址信息。

然后在 User 的映射文件中配置:

<resultMap id="userMap" type="User"><id column="user_id" property="id"/><result column="username" property="username"/><association property="address" select="cn.tx.mapper.AddressMapper.findAddressByUserId" column="user_id" fetchType="lazy"/>
</resultMap>

这样,当查询用户时,只有在访问 user.getAddress() 时才会去查询地址信息。

多对一延迟加载示例
比如多个订单对应一个用户,在查询订单时,用户信息可以延迟加载。在 Order 的映射文件中配置:

<resultMap id="orderMap" type="Order"><id column="order_id" property="id"/><result column="order_no" property="orderNo"/><association property="user" select="cn.tx.mapper.UserMapper.findUserByOrderId" column="user_id" fetchType="lazy"/>
</resultMap>

当访问 order.getUser() 时才会加载用户信息。

一对多延迟加载示例
User 的映射文件中配置如下:

<resultMap id="userMap" type="User"><id column="user_id" property="id"/><result column="username" property="username"/><collection property="orders" select="cn.tx.mapper.OrderMapper.findOrdersByUserId" column="user_id" fetchType="lazy"/>
</resultMap>

只有在调用 user.getOrders() 时,才会执行查询订单的SQL语句。

多对多延迟加载示例
假设 UserRole 是多对多关系,需要通过中间表 user_role 来关联。

首先在 User 的映射文件中配置:

<resultMap id="userMap" type="User"><id column="user_id" property="id"/><result column="username" property="username"/><collection property="roles" select="cn.tx.mapper.RoleMapper.findRolesByUserId" column="user_id" fetchType="lazy"/>
</resultMap>

Role 的映射文件中也做类似配置:

<resultMap id="roleMap" type="Role"><id column="role_id" property="id"/><result column="role_name" property="roleName"/><collection property="users" select="cn.tx.mapper.UserMapper.findUsersByRoleId" column="role_id" fetchType="lazy"/>
</resultMap>

这样在查询用户或角色时,相关联的多对多关系数据只有在实际访问时才会加载。

五、MyBatis缓存

5.1 一级缓存

1. 定义与特性

  • 作用范围SqlSession 级别,默认开启。
  • 生命周期:与 SqlSession 绑定,Session 关闭或执行 commit()/rollback() 时清空。
  • 工作机制:同一个 Session 内多次执行 相同查询,优先从缓存读取。
SqlSession session = sqlSessionFactory.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user1 = mapper.findById(1); // 第一次查询,从数据库获取数据并放入一级缓存
User user2 = mapper.findById(1); // 第二次查询,从一级缓存中获取数据

2. 缓存命中与失效

  • 命中条件:相同的 SQL + 相同的参数 + 相同的环境。
  • 失效场景
    • 执行 INSERT/UPDATE/DELETE 操作。
    • 手动调用 sqlSession.clearCache()
    • SqlSession 的查询不会共享缓存。

3. 实战注意点

  • 坑点:跨方法调用时,若使用不同 SqlSession,一级缓存不共享。
  • 适用场景:短生命周期操作(如单次请求内的重复查询)。

5.2 二级缓存

1. 定义与特性

  • 作用范围Mapper 级别(跨 SqlSession),需手动开启。
  • 生命周期:与应用进程绑定,重启或调用 clear() 时清空。
  • 存储结构:序列化后的数据(需实体类实现 Serializable)。

2. 缓存策略与配置

  • 开启步骤
    <!-- MyBatis 全局配置 -->
    <settings><setting name="cacheEnabled" value="true"/>
    </settings><!-- Mapper XML 中声明 -->
    <mapper namespace="..."><cache eviction="LRU" flushInterval="60000" size="512"/>
    </mapper>
    
  • 淘汰策略eviction):
    • LRU:最近最少使用(默认)。
    • FIFO:先进先出。
    • SOFT:软引用,基于垃圾回收器状态回收。

3. 工作流程

  1. 查询顺序:二级缓存 → 一级缓存 → 数据库。
  2. 数据提交SqlSession 关闭时,一级缓存数据同步到二级缓存。

5.3 一级缓存 vs 二级缓存:核心差异

维度一级缓存二级缓存
作用范围SqlSessionSqlSession(同一 Mapper)
默认状态开启需手动配置
存储位置内存(JVM 堆)可配置为磁盘或第三方缓存(如 Redis)
数据共享不共享多个 Session 共享
生命周期随 Session 销毁长期存在,需主动管理

5.4 缓存使用陷阱与最佳实践**

1. 常见问题

  • 脏读风险:跨服务节点时,二级缓存可能导致数据不一致。
  • 序列化开销:二级缓存序列化影响性能,需权衡缓存粒度。
  • 缓存穿透:频繁查询不存在的数据,需设置空值缓存。

2. 优化建议

  • 合理配置:根据数据更新频率选择缓存策略(如 flushInterval)。
  • 第三方缓存:集成 Redis 或 Ehcache 实现分布式缓存。
  • 注解控制:使用 @CacheNamespace@CacheNamespaceRef 精细化管理。

总结

掌握这些核心技能,可以高效利用MyBatis构建健壮的数据库应用。实际开发中,需根据业务场景选择合适策略,平衡性能与功能需求。

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

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

相关文章

TDesign AI Chat - Vue3.x 可用!腾讯出品的 AIGC 交互对话组件,免费开源、包含设计资源

各位前端开发者有遇到做 AI Chat 项目的聊天交互界面需求了吗&#xff1f;TDesign 出品的这个组件很不错&#xff0c;推荐给大家。 TDesign AI Chat 是 TDesign 为 AIGC 场景开发的 UI 系列组件中的一部分&#xff0c;主要用于开发目前非常流行的 ChatBot 对话交互场景。最近 …

spring -MVC-02

SpringMVC-11 - 响应 在 SpringMVC 中&#xff0c;响应是服务器对客户端请求的反馈&#xff0c;它可以以多种形式呈现&#xff0c;包括视图名称、ModelAndView 对象、JSON 数据以及重定向等。以下是对 SpringMVC 中不同响应类型的详细介绍&#xff1a; 1. 视图名称 通过返回…

老旧设备升级利器:Modbus TCP转 Profinet让能效监控更智能

在工业自动化领域&#xff0c;ModbusTCP和Profinet是两种常见的通讯协议。Profinet是西门子公司推出的基于以太网的实时工业以太网标准&#xff0c;而Modbus则是由施耐德电气提出的全球首个真正开放的、应用于电子控制器上的现场总线协议。这两种协议各有各的优点&#xff0c;但…

ubuntu下docker安装mongodb-支持单副本集

1.mogodb支持事务的前提 1) MongoDB 版本&#xff1a;确保 MongoDB 版本大于或等于 4.0&#xff0c;因为事务支持是在 4.0 版本中引入的。 2) 副本集配置&#xff1a;MongoDB 必须以副本集&#xff08;Replica Set&#xff09;模式运行&#xff0c;即使是单节点副本集&#x…

【前端开发】Uniapp日期时间选择器:实现分钟动态步长设置

技术栈 Uniapp + Vue3 + uView年份显示前后一年,分钟动态设置间隔效果图 主体显示<view class="uni-row-between selector"><view class="uni-flex-1 left" @click="!props.disabled && openPicker()"><uni-iconscolor=…

iOS 蓝牙开发中的 BT 与 BLE

在 iOS 开发者的语境里&#xff0c;大家把 BT 和 BLE 当成两种不同的蓝牙技术在谈——它们来自同一个 Bluetooth 规范&#xff0c;但面向的场景、协议栈乃至 Apple 提供的 API 都截然不同。 缩写全称 / 技术名称规范层叫法iOS 支持现状典型用途BTBluetooth Classic&#xff08…

Flink CEP是什么?

Apache Flink 的 CEP&#xff08;Complex Event Processing&#xff0c;复杂事件处理&#xff09; 是 Flink 提供的一个库&#xff0c;用于在无界数据流中检测符合特定模式的事件组合。 &#x1f3af; 一、什么是 CEP&#xff1f; ✅ 定义&#xff1a; CEP 是一种从连续的数据…

ARM (Attention Refinement Module)

ARM模块【来源于BiSeNet】&#xff1a;细化特征图的注意力&#xff0c;增强重要特征并抑制不重要的特征。 Attention Refinement Module (ARM) 详解 ARM (Attention Refinement Module) 是 BiSeNet 中用于增强特征表示的关键模块&#xff0c;它通过注意力机制来细化特征图&…

AR0144CSSC20SUKA0-CRBR——1/4英寸 1.0 MP 高性能CMOS图像传感器解析

产品概述&#xff1a; AR0144CSSC20SUKA0-CRBR 是一款1/4 英寸&#xff0c;1.0 Mp CMOS 数字图像传感器&#xff0c;带有 1280H x 800V 有效像素阵列 全局快门CMOS数字图像传感器&#xff0c;它结合了新型的创新全局快门像素设计&#xff0c;适用于准确快速的移动场景捕捉。该…

深入理解递归算法:Go语言实现指南

深入理解递归算法&#xff1a;Go语言实现指南 引言 递归是编程中一种优雅而强大的算法思想&#xff0c;通过函数自我调用的方式解决复杂问题。本文将使用Go语言演示递归的核心原理&#xff0c;并通过典型示例帮助开发者掌握这一重要技术。 一、递归基础概念 1.1 递归定义 递归…

vue2实现【瀑布流布局】

瀑布流 1. 解释2. 形成结构和样式3. 自定义指令 1. 解释 瀑布流特征&#xff1a; 等宽不等高&#xff1a;元素宽度固定&#xff0c;高度根据内容自适应。错落排列&#xff1a;元素像瀑布一样从上到下依次填充&#xff0c;自动寻找最短列插入 体现&#xff1a;图中第一排1&…

CSS display有几种属性值

在 CSS 中&#xff0c;display 属性是控制元素布局和渲染方式的核心属性之一。它有多种属性值&#xff0c;每个值都决定了元素在文档流中的表现形式。以下是 display 的主要属性值分类及说明&#xff1a; 1. 块级和行内布局 块级元素 (block) 特性&#xff1a;独占一行&…

基于Java实现可靠传输

实现可靠传输 1. 结合代码和 LOG 文件分析针对每个项目举例说明解决效果。 RDT1.0 对应 Log 日志&#xff1a;Log 1.0.txt&#xff0c;接收文件 recvData 1.0.txt RDT1.0 版本是在可靠信道上进行可靠的数据传输&#xff0c;因此没有过多的内容需要说明&#xff0c;发送方 L…

机器学习10-随机森林

随机森林学习笔记 一、随机森林简介 随机森林&#xff08;Random Forest&#xff09;是一种集成学习算法&#xff0c;基于决策树构建模型。它通过组合多个决策树的结果来提高模型的准确性和稳定性。随机森林的核心思想是利用“集成”的方式&#xff0c;将多个弱学习器组合成一…

LeetCode 438. 找到字符串中所有字母异位词 | 滑动窗口与字符计数数组解法

文章目录 问题描述核心思路&#xff1a;滑动窗口 字符计数数组1. 字符计数数组2. 滑动窗口 算法步骤完整代码实现复杂度分析关键点总结类似问题 问题描述 给定两个字符串 s 和 p&#xff0c;要求找到 s 中所有是 p 的**字母异位词&#xff08;Anagram&#xff09;**的子串的起…

idea中,git的cherry-pick怎么用

背景: A同学在A分支进行开发, B同学在B分支进行开发,B同学开发过程中发现,A同学在A分支上面的某次提交,例如某次提交了一个工具类,B同学也用的到这个工具类,但是B又不想mergeA分支的代码,此时就可以用到git的chery pick能力.

深入解析:如何基于开源OpENer开发EtherNet/IP从站服务

一、EtherNet/IP协议概述 EtherNet/IP(Industrial Protocol)是一种基于以太网的工业自动化通信协议,它将CIP(Common Industrial Protocol)封装在标准以太网帧中,通过TCP/IP和UDP/IP实现工业设备间的通信。作为ODVA(Open DeviceNet Vendors Association)组织的核心协议…

当 PyIceberg 和 DuckDB 遇见 AWS S3 Tables:打造 Serverless 数据湖“开源梦幻组合”

引言 在一些大数据分析场景比如电商大数据营销中&#xff0c;我们需要快速分析存储海量用户行为数据&#xff08;如浏览、加购、下单&#xff09;&#xff0c;以进行用户行为分析&#xff0c;优化营销策略。传统方法依赖 Spark/Presto 集群或 Redshift 查询 S3 上的 Parquet/O…

流复备机断档处理

文章目录 环境症状问题原因解决方案 环境 系统平台&#xff1a;UOS&#xff08;海光&#xff09;,UOS &#xff08;飞腾&#xff09;,UOS&#xff08;鲲鹏&#xff09;,UOS&#xff08;龙芯&#xff09;,UOS &#xff08;申威&#xff09;,银河麒麟svs&#xff08;X86_64&…

【蓝桥杯真题精讲】第 16 届 Python A 组(省赛)

文章目录 T1 偏蓝 (5/5)T2 IPv6 (0/5)T3 2025 图形 (10/10)T4 最大数字 (10/10)T5 倒水 (15/15)T6 拼好数 (0/15)T7 登山 (20/20)T8 原料采购 (20/20) 更好的阅读体验 高速访问&#xff1a;https://wiki.dwj601.cn/ds-and-algo/lan-qiao-cup/16th-python-a/永久链接&#xff1…