当前位置: 首页 > news >正文

Mybatis的高级特性

MyBatis 核心进阶


缓存机制

        MyBatis 缓存是位于应用层的数据暂存区,通过减少数据库访问次数提升性能

一级缓存

        本地缓存--默认当前启用的线程绑定缓存,生命周期与 SqlSession 一致

运行流程

SqlSession查询 → 缓存是否存在 → (存在)返回缓存结果、(不存在)查询数据库 → 结果存入缓存中

导致失效的操作
  • 执行任意 UPDATE 操作
  • 手动调用 clearCache()
  • 会话关闭

二级缓存

应用级缓存--用于在多个  SqlSession  之间共享数据,减少数据库访问次数,提升查询性能

二级缓存默认为关闭,需要手动开启

开启操作
        全局配置

在 MyBatis 配置文件中启用缓存( <setting name="cacheEnabled" value="true"/> )

        Mapper配置

在映射 XML 中添加  <cache> ,可自定义参数(如  eviction 、 size 、 flushInterval  等)

        查询控制

若某条 SQL 不想用缓存,通过  useCache="true/false"  控制特定查询是否使用缓存

工作原理

查询顺序为二级缓存 → 一级缓存 → 数据库,命中二级缓存则直接返回结果。默认实现为  PerpetualCache ,支持自定义实现(如集成 Redis、EhCache 等)以满足分布式

缓存应用三定律

数据变动频率高 → 禁用缓存

查询耗时大于1ms → 考虑一级缓存

QPS > 100且数据稳定 → 启用二级缓存

 用注解代替XML

        每写一条SQL,都要写对应实体类Dao的XML文件做配置,是不是很复杂?现在一般情况较少使用XML这种传统的配置方式了,我们可以通过注解来做配置

配置方法        

        创建 Mapper 接口

直接在接口方法上使用 SQL 注解,不需要 XML 文件

public interface UserMapper {// 简单查询@Select("SELECT * FROM users WHERE id = #{id}")User selectById(int id);// 插入(返回自增ID)@Insert("INSERT INTO users(name, email) VALUES(#{name}, #{email})")@Options(useGeneratedKeys = true, keyProperty = "id")int insert(User user);// 更新@Update("UPDATE users SET name=#{name} WHERE id=#{id}")int updateName(@Param("id") int id, @Param("name") String name);// 删除@Delete("DELETE FROM users WHERE id = #{id}")int delete(int id);
}

若遇到数据表名与接口名对不上的情况,解决方式与xml相似

public interface UserDao {@Results(id = "userMap",value = {@Result(id = true ,column = "id" ,property = "userId"),@Result(column = "username" ,property = "userName"),@Result(column = "birthday" ,property = "userBirthday"),@Result(column = "sex" ,property = "userSex"),@Result(column = "address" ,property = "userAddress")})@Select("select * from user where id = #{id}")public User findUserById(Integer id);

在测试类尝试运行,与XML文件无异

总结及补充

核心思路:在 Mapper 接口方法上直接使用 @Select@Insert 等注解替代 XML。

动态 SQL:通过 @SelectProvider 配合 Java 类实现。

关联映射:用 @Results + @Result + @One/@Many 配置。

适用性优先用于简单 CRUD,复杂场景仍建议结合 XML。

在适用场景上

简单 CRUD:直接使用 @Select/@Insert 等

中等复杂 SQL:用 @SelectProvider + SQL 工具类

复杂动态 SQL仍推荐 XML(注解中拼接 SQL 可读性差)

与XML优缺点对比

优点缺点
代码更集中(SQL 与 Java 在一起)复杂 SQL 可读性差
编译时检查 SQL 语法动态 SQL 编写繁琐
避免 XML 文件切换关联映射配置冗长
适合简单操作多表联查建议用 XML

插件机制

在四大对象(Executor、StatementHandler、ParameterHandler、ResultSetHandler)的方法前后插入自己的逻辑,例如:
• 打印 SQL 耗时
• 动态改写 SQL(加 limit、加租户字段)
• 参数加解密

        

通过实现 Interceptor 接口,在类上加 @Intercepts 与 @Signature 指定拦截点。

用途:分页、审计日志、性能监控

批处理

一次网络往返发送多条 SQL,提高写入/更新吞吐量。

批处理黄金公式
最佳批次大小 = 数据库最大包大小 / 单行数据大小

MySQL默认max_allowed_packet=4MB

配置

JDBC URL 加参数:

jdbc:mysql://localhost:3306/mybatis?allowMultiQueries=true&rewriteBatchedStatements=true

 Mapper.xml:多条语句用分号隔开

<update id="batchUpdate"><foreach collection="list" item="u" separator=";">UPDATE user SET age = #{u.age} WHERE id = #{u.id}</foreach>
</update>

Java 调用测试

List<User> list = Arrays.asList(new User(1,22), new User(2,23));
sqlSession.update("batchUpdate", list);

相当于生成了

UPDATE user SET age = 22 WHERE id = 1;  
UPDATE user SET age = 23 WHERE id = 2;

延迟加载

只在真正用到关联对象时才发出第二条 SQL,避免一次查太多数据。

配置

 mybatis-config.xml 全局开关

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

Mapper.xml

fetchType="lazy" 可局部覆盖

<resultMap id="userMap" type="User"><id column="id" property="id"/><collection property="orders" ofType="Order"column="id" select="selectOrdersByUserId"fetchType="lazy"/>
</resultMap>

Java示例

try (SqlSession session = factory.openSession()) {UserMapper mapper = session.getMapper(UserMapper.class);User u = mapper.selectById(1);   // ① 只发一条 SQL:SELECT * FROM user WHERE id=1System.out.println(u.getName()); // ② 仍不发 orders SQLList<Order> orders = u.getOrders(); // ③ 第一次访问触发第二条 SQLSystem.out.println(orders.size());
}

第①步只查  user  表。
第③步第一次用到  orders  时,

MyBatis 自动补发  SELECT * FROM orders WHERE user_id = 1 ,完成延迟加载。

Generator代码生成器

MyBatis Generator 可以通过数据库表结构 构建 Java 领域模型 + DAO 层代码

graph TB
DB[数据库] -->|JDBC| MBG[MyBatis Generator]
MBG -->|模板引擎| POJO[实体类]
MBG --> MapperInt[Mapper接口]
MBG --> MapperXML[Mapper XML]
MBG --> Example[查询条件类]

工作流程

数据库数据源 → MyBatis Generator   → 模板引擎、pojo实体类、Mapper接口、XML、条件类

在配置这个之前,需要添加相关的依赖

<dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper</artifactId><version>5.2.0</version></dependency>

文件(必须要叫generatorConfig.xml)

generatorConfig.xml(这个很重要)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfigurationPUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN""http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration><!--targetRuntime: 执行生成的逆向工程的版本MyBatis3Simple: 生成基本的CRUD(低配)MyBatis3: 生成带条件的CRUD(高配)--><context id="DB2Tables" targetRuntime="MyBatis3"><!-- 数据库的连接信息 --><jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"connectionURL="jdbc:mysql://localhost:3306/mybatis?currentSchema=mybatis&amp;useSSL=false&amp;serverTimezone=UTC"userId="root"password="123456"></jdbcConnection><!-- javaBean的生成策略--><javaModelGenerator targetPackage="com.noy.pojo" targetProject="src\main\java"><!--是否生成子包。如果为true com.noy.pojo生成的保姆那个带有层级
目录false  com.noy.pojo就是一个包名--><property name="enableSubPackages" value="true" /><!--通过数据表字段生成pojo。如果字段名称带空格,会去掉空格--><property name="trimStrings" value="true" /></javaModelGenerator><!-- SQL映射文件的生成策略 --><sqlMapGenerator targetPackage="com.noy" targetProject="src\main\resources"><property name="enableSubPackages" value="true" /></sqlMapGenerator><!-- Mapper接口的生成策略 --><javaClientGenerator type="XMLMAPPER"targetPackage="com.noy" targetProject="src\main\java"><property name="enableSubPackages" value="true" /></javaClientGenerator><!-- 逆向分析的表 --><!-- tableName设置为*号,可以对应所有表,此时不写
domainObjectName --><!-- domainObjectName属性指定生成出来的实体类的类名 --><table tableName="`user`"domainObjectName="User"catalog="mybatis">
<!--               schema="mybatis">--><generatedKey column="id" sqlStatement="JDBC" identity="true"/></table></context>
</generatorConfiguration>

填入数据库以及开发与resources的文件生成策略(即格式)之后,可以点击maven的

开始生成代码

生成无误之后可以在测试包添加测试

@Testpublic void test01() throws Exception {//selectByExample为根据条件筛选,若其中填null,则全盘扫描List<User> userList = userMapper.selectByExample(null);for (User user : userList) {System.out.println(user);}}

测试代码生成器生成的功能,这样可以让我们在传统的crud中节省更多的精力

分页查询

将数据库中的大量数据分割成多个小块(页)进行展示,避免一次性加载成千上万条数据,减少单次查询的数据传输量,降低数据库查询负载

提升用户体验  减轻服务器压力  优化性能

分页流程

接收参数:获取前端传入的 pageNum 和 pageSize

计算偏移offset = (pageNum-1) * pageSize

执行查询

物理分页:通过数据库的 LIMIT/ROWNUM 实现/插件分页:使用 PageHelper.startPage()

返回结果:包装为包含分页信息的对象(总条数/总页数等)

最好不要在内存中处理大数据分页,让数据库完成分页截取

核心参数

参数名作用示例计算公式
pageNum当前页码第3页前端传入
pageSize每页显示条数每页10条常取10/20/50
total总数据量125条SELECT COUNT(*)
offset数据起始位置第20条开始(pageNum-1)*pageSize

基础分页实现方式

物理分页(推荐)

在数据库层面截取数据片段,性能高、内存消耗小

-- MySQL
SELECT * FROM products 
ORDER BY create_time DESC
LIMIT #{offset}, #{pageSize}  -- 从offset位置取pageSize条-- Oracle
SELECT * FROM (SELECT t.*, ROWNUM rn FROM (SELECT * FROM products ORDER BY create_time DESC) t WHERE ROWNUM <= #{end}
) WHERE rn >= #{start}

逻辑分页

先获取全部数据,再在内存中截取,数据量大会导致内存溢出、传输全部数据浪费网络带宽

// MyBatis RowBounds示例(已过时)
List<Product> list = sqlSession.selectList("selectProducts", null, new RowBounds(offset, pageSize) // 内存分页
);


示例

实体类

public class Product {private Long id;private String name;private Double price;// 构造方法/getter/setter 省略
}

 Mapper 接口

@Mapper
public interface ProductMapper {// 手写分页查询List<Product> selectByPage(@Param("offset") int offset, @Param("pageSize") int pageSize);// 获取总数Long selectTotalCount();// PageHelper 查询(无需特殊参数)List<Product> selectAll();
}

Mapper XML

<!-- 手写分页 -->
<select id="selectByPage" resultType="Product">SELECT id, name, priceFROM productsORDER BY id DESCLIMIT #{offset}, #{pageSize}
</select><!-- 总数统计 -->
<select id="selectTotalCount" resultType="Long">SELECT COUNT(1) FROM products
</select><!-- PageHelper 查询 -->
<select id="selectAll" resultType="Product">SELECT id, name, priceFROM productsORDER BY id DESC
</select>

分页结果封装类

public class PageResult<T> {private int pageNum;     // 当前页码private int pageSize;    // 每页条数private long total;      // 总记录数private int pages;       // 总页数private List<T> list;    // 当前页数据public PageResult(int pageNum, int pageSize, long total, List<T> list) {this.pageNum = pageNum;this.pageSize = pageSize;this.total = total;this.list = list;this.pages = (int) Math.ceil((double) total / pageSize);}// Getter 省略
}

服务层实现 仅展示手写分页

// 手写分页public PageResult<Product> getProductsManual(int pageNum, int pageSize) {int offset = (pageNum - 1) * pageSize;List<Product> list = productMapper.selectByPage(offset, pageSize);long total = productMapper.selectTotalCount();return new PageResult<>(pageNum, pageSize, total, list);}

Test类  仅展示手写分页

/*** 测试手写分页*/@Testvoid testManualPagination() {// 请求第2页,每页10条PageResult<Product> result = productService.getProductsManual(2, 10);System.out.println("===== 手写分页结果 =====");System.out.println("当前页: " + result.getPageNum());System.out.println("每页条数: " + result.getPageSize());System.out.println("总记录数: " + result.getTotal());System.out.println("总页数: " + result.getPages());System.out.println("当前页数据: ");result.getList().forEach(p -> System.out.printf("| %2d | %-20s | %.2f |%n", p.getId(), p.getName(), p.getPrice()));}


http://www.dtcms.com/a/318077.html

相关文章:

  • 【自动化运维神器Ansible】playbook核心组件之tags深度解析
  • 第一性原理科学计算服务器如何选择配置-CPU选择篇
  • thinkpad E14重装win 10系统
  • 云端软件工程智能代理:任务委托与自动化实践全解
  • Spring Boot Actuator 监控功能的简介及禁用
  • Java面试题036:一文深入了解VUE(1)
  • 批量提问程序开发方案:基于Python的百度文小言接口实现
  • 学习嵌入式之硬件——ARM体系
  • vue margin与padding对比
  • 用户体验设计中微投入设计:用户不知不觉付出的 3 种方式
  • 【24】C++实战篇——【 C++ 外部变量】 C++多个文件共用一个枚举变量,外部变量 extern,枚举外部变量 enum
  • Kaggle 经典竞赛泰坦尼克号:超级无敌爆炸详细基础逐行讲解Pytorch实现代码,看完保证你也会!!!
  • 直播间自动发言工具的开发
  • OpenAI/gpt-oss开源模型部署与使用全指南
  • 三维偏序 -- cdq 套 cdq
  • 蓝桥杯----锁存器、LED、蜂鸣器、继电器、Motor
  • 【YOLOv8改进 - C2f融合】C2f融合EBlock(Encoder Block):低光增强编码器块,利用傅里叶信息增强图像的低光条件
  • 分类数据集 - 水稻叶病虫害分类数据集下载
  • Java面试宝典:Java内存模型与对象可达性判定原理
  • Python爬虫实战:研究spiderfoot工具,构建网络情报收集系统
  • java 之 继承
  • jdk动态代理如何实现
  • 【补题】Codeforces Round 779 (Div. 2) C. Shinju and the Lost Permutation
  • 【补题】CodeTON Round 1 (Div. 1 + Div. 2, Rated, Prizes!) D. K-good
  • 大数据之HBase
  • 深度学习-卷积神经网络CNN-多输入输出通道
  • MySQL数据库索引及底层数据结构
  • 宝塔部署go 项目
  • Maven--打包方式详解 (pom、war、jar)
  • 各类排序算法