Mybatis 项目
https://gitee.com/supervol/loong-springboot-study
(记得给个start,感谢)
Mybatis 概述
在 Spring Boot 3 环境中,MyBatis 作为一款轻量级持久层框架,凭借其对 SQL 的灵活控制和与 Spring 生态的良好兼容性,成为数据访问层的常用选择。
Mybatis 核心
MyBatis 专注于解决 JDBC 操作的痛点(如手动处理连接、结果集映射等),同时保留开发者对 SQL 的直接控制权,核心特性包括:
- SQL 与代码解耦:通过 XML 或注解定义 SQL,避免硬编码;
- 灵活的映射机制:支持对象与数据库表的自动映射(如下划线转驼峰),也可手动配置复杂映射(一对一、一对多关联);
- 动态 SQL:通过标签(
if
、choose
、foreach
等)根据条件动态生成 SQL,简化复杂查询; - 轻量级:无侵入性,核心依赖少,性能接近原生 JDBC;
- 与 Spring 无缝集成:通过
mybatis-spring-boot-starter
快速整合到 Spring Boot 3 中,无需手动管理SqlSession
等组件。
Mybatis 对比
MyBatis 和 JPA(Java Persistence API)是 Java 生态中最常用的两种持久层技术,它们的设计理念和适用场景有显著差异。
1. 定位与理念
维度 | MyBatis | JPA |
---|---|---|
定位 | 半自动 ORM 框架(SQL 与对象映射分离) | ORM 规范(通常指 Hibernate 等实现,全自动 ORM) |
设计理念 | 聚焦 SQL 控制,保留开发者对 SQL 的直接管理权 | 聚焦对象模型,通过面向对象的方式操作数据库 |
核心目标 | 简化 JDBC 操作,同时不丢失 SQL 的灵活性 | 消除 SQL 依赖,让开发者以 "操作对象" 的方式操作数据库 |
2. 核心功能对比
(1)SQL 控制能力
MyBatis:完全开放 SQL 控制权,支持 手写 SQL(XML 或注解),开发者可直接编写优化后的原生 SQL、存储过程、复杂联合查询等。例:通过 XML 或
@Select
注解直接定义 SQL:JPA:屏蔽 SQL 细节,通过 JPQL(面向对象的查询语言) 或 Criteria API 操作数据,SQL 由框架(如 Hibernate)自动生成。例:通过 JPQL 查询:
@Query("SELECT u FROM User u WHERE u.age > :minAge ORDER BY u.createTime DESC") List
findByAgeGreaterThan(@Param("minAge") int minAge); 复杂场景下也可手写原生 SQL,但违背 JPA "屏蔽 SQL" 的设计初衷。
(2)映射关系
MyBatis:需手动配置 结果映射(ResultMap) 处理对象关联(一对一、一对多等),灵活性高但配置繁琐。例:一对多映射:
JPA:通过注解(
@OneToOne
、@OneToMany
等)自动维护关联关系,框架会自动生成关联查询的 SQL。例:一对多映射:@Entity public class User {@Idprivate Long id;@OneToMany(mappedBy = "user") // 自动关联 Order 中的 user 字段private List
orders; }
(3)开发效率
MyBatis:
- 简单 CRUD 需手动编写 SQL(或通过代码生成工具生成),开发速度较慢;
- 复杂查询时,手写 SQL 反而更高效(避免框架生成冗余 SQL)。
JPA:
- 基于 Spring Data JPA 时,通过继承
JpaRepository
可直接获得 CRUD、分页、排序等功能,无需编写实现; - 例:
public interface UserRepository extends JpaRepository<User, Long>
即可直接调用findAll()
、save()
等方法; - 简单业务场景下开发效率极高,复杂场景需额外配置(如自定义 JPQL)。
- 基于 Spring Data JPA 时,通过继承
(4)性能与优化
MyBatis:
- 性能接近原生 JDBC,因为 SQL 可控,可针对性优化(如索引利用、查询字段精简);
- 无额外缓存(需手动集成 Redis 等),但避免了框架级缓存的复杂性。
JPA(以 Hibernate 为例):
- 自动生成的 SQL 可能存在冗余(如关联查询过度加载),需通过
fetch = FetchType.LAZY
等配置优化; - 内置一级缓存(Session 级)和二级缓存(全局),可减少重复查询,但缓存配置不当易引发数据一致性问题。
- 自动生成的 SQL 可能存在冗余(如关联查询过度加载),需通过
(5)学习曲线
MyBatis:门槛低,对熟悉 SQL 的开发者友好,核心只需掌握 Mapper 接口、XML 映射规则即可。
JPA:门槛较高,需理解 ORM 核心概念(如持久化上下文、脏检查)、JPQL 语法、关联映射策略等,初期学习成本高。
3. 适用场景
框架 | 适用场景 | 不适用场景 |
---|---|---|
MyBatis | 1. 需精细控制 SQL(如复杂报表、多表联合查询);2. 数据库设计不规范(字段名与对象属性差异大);3. 团队熟悉 SQL 优化;4. 对性能要求极高的场景。 | 1. 快速开发简单 CRUD 业务;2. 团队更擅长面向对象编程而非 SQL。 |
JPA | 1. 业务简单(以 CRUD 为主),需快速迭代;2. 数据库设计规范,适合 ORM 映射;3. 团队偏好面向对象编程,希望减少 SQL 编写。 | 1. 复杂 SQL 场景(如多表嵌套查询、存储过程);2. 需深度优化 SQL 性能的场景。 |
Mybatis 示例
请参考项目地址中 springboot-orm/springboot-mybatis 模块代码。
Mybatis 集成
1. 添加依赖
在 pom.xml
中引入核心依赖:
org.springframework.boot spring-boot-starter-parent 3.2.0
org.springframework.boot spring-boot-starter-web org.mybatis.spring.boot mybatis-spring-boot-starter 3.0.3 com.mysql mysql-connector-j runtime com.zaxxer HikariCP org.springframework.boot spring-boot-starter-test test
2. 核心配置
在 src/main/resources/application.yaml
中配置:
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=trueusername: rootpassword: 123456hikari: # HikariCP 配置maximum-pool-size: 10 # 最大连接数minimum-idle: 5 # 最小空闲连接
mybatis:mapper-locations: classpath:mybatis/mappers/*.xml # Mapper XML 文件位置type-aliases-package: com.example.demo.entity # 实体类别名包(简化 XML 中的类名)configuration:map-underscore-to-camel-case: true # 开启下划线转驼峰(如数据库字段 user_name → 实体类 userName)log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印 SQL 日志(开发环境用)
3. 创建实体类
package com.example.demo.entity;
public class User {private Long id;private String username; // 对应数据库 user_name(下划线转驼峰生效)private Integer age;// 省略 getter、setter、toString
}
4. 创建 Mapper 接口
定义数据操作方法:
package com.example.demo.mapper;
import com.example.demo.entity.User;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
// @Mapper 标识为 MyBatis 映射接口(或在启动类用 @MapperScan 批量扫描)
@Mapper
public interface UserMapper {// 查询所有用户List findAll();// 根据 ID 查询User findById(Long id);// 新增用户int insert(User user);// 更新用户int update(User user);// 删除用户int deleteById(Long id);
}
5. 编写 Mapper XML
在 src/main/resources/mybatis/mappers/UserMapper.xml
中编写 SQL:
INSERT INTO user (username, age) VALUES (#{username}, #{age}) UPDATE user SET username = #{username}, age = #{age} WHERE id = #{id} DELETE FROM user WHERE id = #{id}
6. 编写核心代码
// Service 层(业务逻辑)
package com.example.demo.service;
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserService {private final UserMapper userMapper;// 构造器注入(Spring Boot 3 推荐)public UserService(UserMapper userMapper) {this.userMapper = userMapper;}public List getAllUsers() {return userMapper.findAll();}public User getUserById(Long id) {return userMapper.findById(id);}public void addUser(User user) {userMapper.insert(user);}
}
// Controller 层(接口暴露)
package com.example.demo.controller;
import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/users")
public class UserController {private final UserService userService;public UserController(UserService userService) {this.userService = userService;}@GetMappingpublic List getAll() {return userService.getAllUsers();}@GetMapping("/{id}")public User getById(@PathVariable Long id) {return userService.getUserById(id);}@PostMappingpublic String add(@RequestBody User user) {userService.addUser(user);return "新增成功,ID:" + user.getId();}
}
7. 启动类与测试
package com.example.demo;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
// @MapperScan 批量扫描 Mapper 接口(替代每个接口加 @Mapper)
@SpringBootApplication
@MapperScan("com.example.demo.mapper")
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}
}
启动后,访问 http://localhost:8080/users
即可测试接口。
Mybatis 高级
1. 动态 SQL
通过 <if>、<choose>、<foreach>
等标签动态生成 SQL:
2. 分页查询
添加依赖:
com.github.pagehelper pagehelper-spring-boot-starter 1.4.6
使用方式:
// Service 中
public PageInfo getUsersByPage(int pageNum, int pageSize) {PageHelper.startPage(pageNum, pageSize); // 分页拦截List users = userMapper.findAll();return new PageInfo<>(users); // 封装分页信息
}
3. 事务管理
通过 Spring 的 @Transactional
注解控制事务:
@Service
public class UserService {// ...@Transactional // 方法内所有操作要么全成功,要么全回滚public void batchAdd(List users) {for (User user : users) {userMapper.insert(user);}}
}
4. 多数据源配置
通过 @Configuration
配置多个数据源,配合 @MapperScan
指定不同 Mapper 对应的数据源:
@Configuration
public class DataSourceConfig {@Primary // 默认数据源@Bean@ConfigurationProperties("spring.datasource.first")public DataSource firstDataSource() {return DataSourceBuilder.create().build();}@Bean@ConfigurationProperties("spring.datasource.second")public DataSource secondDataSource() {return DataSourceBuilder.create().build();}
}
Mybatis 注意
- 版本兼容性:MyBatis-Spring-Boot-Starter 需使用 3.x 版本(适配 Spring Boot 3);
- 包名迁移:Spring Boot 3 基于 Jakarta EE,若使用
javax.persistence
相关注解,需替换为jakarta.persistence
; - Mapper 扫描:确保
@Mapper
或@MapperScan
正确配置,否则会出现 "No qualifying bean" 错误; - SQL 日志:生产环境建议关闭
log-impl
配置,避免性能损耗。