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:XML<select id="findByAge" resultType="User"> SELECT * FROM user WHERE age > #{minAge} ORDER BY create_time DESC </select>
-
JPA :屏蔽 SQL 细节,通过 JPQL(面向对象的查询语言) 或 Criteria API 操作数据,SQL 由框架(如 Hibernate)自动生成。例:通过 JPQL 查询:
java@Query("SELECT u FROM User u WHERE u.age > :minAge ORDER BY u.createTime DESC") List<User> findByAgeGreaterThan(@Param("minAge") int minAge);
复杂场景下也可手写原生 SQL,但违背 JPA "屏蔽 SQL" 的设计初衷。
(2)映射关系
-
MyBatis :需手动配置 结果映射(ResultMap) 处理对象关联(一对一、一对多等),灵活性高但配置繁琐。例:一对多映射:
XML<resultMap id="UserWithOrders" type="User"> <id property="id" column="user_id"/> <collection property="orders" ofType="Order"> <id property="id" column="order_id"/> <result property="amount" column="amount"/> </collection> </resultMap>
-
JPA :通过注解(
@OneToOne
、@OneToMany
等)自动维护关联关系,框架会自动生成关联查询的 SQL。例:一对多映射:java@Entity public class User { @Id private Long id; @OneToMany(mappedBy = "user") // 自动关联 Order 中的 user 字段 private List<Order> 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
中引入核心依赖:
java
<!-- Spring Boot 父工程 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
<relativePath/>
</parent>
<dependencies>
<!-- Spring Boot Web(可选,用于接口测试) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MyBatis 集成 Spring Boot 的 starter -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version> <!-- 适配 Spring Boot 3 -->
</dependency>
<!-- 数据库驱动(以 MySQL 为例) -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 数据源(HikariCP,Spring Boot 3 默认) -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>
<!-- 测试依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2. 核心配置
在 src/main/resources/application.yaml
中配置:
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
username: root
password: 123456
hikari: # 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. 创建实体类
java
package com.example.demo.entity;
public class User {
private Long id;
private String username; // 对应数据库 user_name(下划线转驼峰生效)
private Integer age;
// 省略 getter、setter、toString
}
4. 创建 Mapper 接口
定义数据操作方法:
java
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<User> 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:
java
<?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">
<!-- namespace 对应 Mapper 接口全类名 -->
<mapper namespace="com.example.demo.mapper.UserMapper">
<!-- 查所有 -->
<select id="findAll" resultType="User"> <!-- resultType 用别名(实体类名) -->
SELECT id, username, age FROM user
</select>
<!-- 按 ID 查 -->
<select id="findById" parameterType="Long" resultType="User">
SELECT id, username, age FROM user WHERE id = #{id}
</select>
<!-- 新增 -->
<insert id="insert" parameterType="User" useGeneratedKeys="true" keyProperty="id">
INSERT INTO user (username, age) VALUES (#{username}, #{age})
</insert>
<!-- 更新 -->
<update id="update" parameterType="User">
UPDATE user SET username = #{username}, age = #{age} WHERE id = #{id}
</update>
<!-- 删除 -->
<delete id="deleteById" parameterType="Long">
DELETE FROM user WHERE id = #{id}
</delete>
</mapper>
6. 编写核心代码
java
// 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<User> 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;
}
@GetMapping
public List<User> getAll() {
return userService.getAllUsers();
}
@GetMapping("/{id}")
public User getById(@PathVariable Long id) {
return userService.getUserById(id);
}
@PostMapping
public String add(@RequestBody User user) {
userService.addUser(user);
return "新增成功,ID:" + user.getId();
}
}
7. 启动类与测试
java
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:
XML
<select id="findByCondition" parameterType="User" resultType="User">
SELECT * FROM user
<where>
<if test="username != null and username != ''">
AND username LIKE CONCAT('%', #{username}, '%')
</if>
<if test="age != null">
AND age = #{age}
</if>
</where>
</select>
2. 分页查询
添加依赖:
XML
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.6</version>
</dependency>
使用方式:
java
// Service 中
public PageInfo<User> getUsersByPage(int pageNum, int pageSize) {
PageHelper.startPage(pageNum, pageSize); // 分页拦截
List<User> users = userMapper.findAll();
return new PageInfo<>(users); // 封装分页信息
}
3. 事务管理
通过 Spring 的 @Transactional
注解控制事务:
@Service
public class UserService {
// ...
@Transactional // 方法内所有操作要么全成功,要么全回滚
public void batchAdd(List<User> users) {
for (User user : users) {
userMapper.insert(user);
}
}
}
4. 多数据源配置
通过 @Configuration
配置多个数据源,配合 @MapperScan
指定不同 Mapper 对应的数据源:
java
@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
配置,避免性能损耗。