MyBatis 从入门到进阶:数据库操作全指南
在 Java 后端开发中,数据库交互是核心环节之一。JDBC 作为 Java 原生的数据库操作方案,流程繁琐且代码冗余,而 MyBatis 作为优秀的持久层框架,能极大简化数据库操作。本文将从 MyBatis 基础入门到进阶应用,结合实战案例全面讲解其使用方法与核心特性。
一、MyBatis 入门:告别 JDBC 的繁琐
1.1 为什么选择 MyBatis?
回顾 JDBC 操作流程,从创建连接池、获取连接,到编写 SQL、绑定参数、处理结果集,再到释放资源,整个过程需要大量重复代码。以添加图书为例,仅基础操作就需数十行代码,且参数绑定、资源清理等逻辑在每个方法中都要重复实现。
MyBatis 的出现完美解决了这些问题:它是一款专注于简化 JDBC 开发的持久层框架,前身为 Apache 的 iBatis 项目,2010 年迁移后更名并持续迭代。其核心优势在于将 SQL 与 Java 代码解耦,自动完成参数映射与结果封装,大幅减少模板代码。
1.2 入门环境搭建
(1)项目初始化
创建 Spring Boot 工程,引入 MyBatis 起步依赖与 MySQL 驱动:
<!-- MyBatis依赖 -->
<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>3.0.3</version>
</dependency>
<!-- MySQL驱动 -->
<dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope>
</dependency>
注意:Spring Boot 3.x 对应 MyBatis 3.x 版本,版本兼容关系可参考官方文档。
(2)数据库与实体类准备
以图书管理系统为例,创建数据库与表结构:
-- 创建数据库
DROP DATABASE IF EXISTS book_test;
CREATE DATABASE book_test DEFAULT CHARACTER SET utf8mb4;
USE book_test;-- 图书表
CREATE TABLE `book_info` (`id` INT NOT NULL AUTO_INCREMENT,`book_name` VARCHAR(127) NOT NULL,`author` VARCHAR(127) NOT NULL,`count` INT NOT NULL,`price` DECIMAL(7,2) NOT NULL,`publish` VARCHAR(256) NOT NULL,`status` TINYINT DEFAULT 1 COMMENT '0-无效,1-可借阅,2-不可借阅',`create_time` DATETIME DEFAULT now(),`update_time` DATETIME DEFAULT now() ON UPDATE now(),PRIMARY KEY (`id`)
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4;
创建对应实体类BookInfo
(使用 Lombok 简化 getter/setter):
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;@Data
public class BookInfo {private Integer id;private String bookName; // 对应表中book_name字段private String author;private Integer count;private BigDecimal price;private String publish;private Integer status;private Date createTime; // 对应表中create_time字段private Date updateTime;
}
(3)数据库配置
在application.yml
中配置数据库连接与 MyBatis 基础参数:
spring:datasource:url: jdbc:mysql://127.0.0.1:3306/book_test?characterEncoding=utf8&useSSL=falseusername: rootpassword: rootdriver-class-name: com.mysql.cj.jdbc.Driver # MySQL 8.x驱动mybatis:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印SQL日志map-underscore-to-camel-case: true # 驼峰命名自动转换
1.3 基础 CRUD 实现
MyBatis 提供注解与 XML 两种开发方式,简单场景推荐注解,复杂 SQL 建议使用 XML。
(1)注解方式
创建 Mapper 接口,通过注解编写 SQL:
import org.apache.ibatis.annotations.*;
import java.util.List;@Mapper // 标识为MyBatis映射接口
public interface BookInfoMapper {// 新增图书@Insert("insert into book_info(book_name,author,count,price,publish,status) " +"values(#{bookName},#{author},#{count},#{price},#{publish},#{status})")@Options(useGeneratedKeys = true, keyProperty = "id") // 返回自增主键Integer insertBook(BookInfo bookInfo);// 根据ID查询@Select("select * from book_info where id=#{id} and status<>0")BookInfo queryById(Integer id);// 更新图书@Update("update book_info set book_name=#{bookName},author=#{author} where id=#{id}")Integer updateBook(BookInfo bookInfo);// 删除图书(逻辑删除)@Delete("update book_info set status=0 where id=#{id}")Integer deleteBook(Integer id);
}
(2)XML 方式
当 SQL 逻辑复杂时,XML 方式更易维护。首先在配置文件中指定 XML 路径:
mybatis:mapper-locations: classpath:mapper/**Mapper.xml # XML文件存放路径
创建BookInfoMapper.xml
文件:
<?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"><!-- 命名空间对应Mapper接口全路径 -->
<mapper namespace="com.example.demo.mapper.BookInfoMapper"><!-- 新增图书 --><insert id="insertBook" useGeneratedKeys="true" keyProperty="id">insert into book_info(book_name,author,count,price,publish,status)values(#{bookName},#{author},#{count},#{price},#{publish},#{status})</insert><!-- 动态更新图书 --><update id="updateBook">update book_info<set><if test="bookName!=null">book_name=#{bookName},</if><if test="author!=null">author=#{author},</if><if test="price!=null">price=#{price},</if></set>where id=#{id}</update>
</mapper>
(3)单元测试
使用 Spring Boot Test 验证功能:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest
class BookInfoMapperTest {@Autowiredprivate BookInfoMapper bookInfoMapper;@Testvoid testInsert() {BookInfo book = new BookInfo();book.setBookName("MyBatis实战");book.setAuthor("张三");book.setCount(10);book.setPrice(new BigDecimal("59.9"));book.setPublish("机械工业出版社");book.setStatus(1);bookInfoMapper.insertBook(book);System.out.println("新增图书ID:" + book.getId()); // 自增ID已赋值}
}
二、MyBatis 进阶:动态 SQL 与实战技巧
2.1 动态 SQL 核心标签
动态 SQL 是 MyBatis 的强大特性,能根据条件动态拼接 SQL,常见标签如下:
(1)<if>:条件判断
用于实现可选字段的动态处理,例如注册用户时的非必填字段:
<insert id="insertUser">INSERT INTO user_info (username,password,<if test="gender!=null">gender,</if>phone)VALUES (#{username},#{password},<if test="gender!=null">#{gender},</if>#{phone})
</insert>
注意:test
属性中引用的是实体类属性,而非数据库字段。
(2)<trim>:去除多余符号
解决多条件拼接时的逗号、AND 等多余符号问题,例如动态新增:
<insert id="insertUserByTrim">INSERT INTO user_info<trim prefix="(" suffix=")" suffixOverrides=","><if test="username!=null">username,</if><if test="password!=null">`password`,</if><if test="phone!=null">phone,</if></trim>VALUES<trim prefix="(" suffix=")" suffixOverrides=","><if test="username!=null">#{username},</if><if test="password!=null">#{password},</if><if test="phone!=null">#{phone},</if></trim>
</insert>
prefix
:添加前缀suffix
:添加后缀suffixOverrides
:去除末尾多余符号
(3)<where>:动态条件拼接
自动处理 WHERE 子句开头的 AND/OR,例如多条件查询:
<select id="queryByCondition" resultType="BookInfo">select * from book_info<where><if test="bookName!=null">and book_name like concat('%',#{bookName},'%')</if><if test="author!=null">and author=#{author}</if><if test="status!=null">and status=#{status}</if></where>
</select>
(4)<foreach>:集合遍历
用于批量操作,例如批量删除:
<delete id="batchDelete">delete from book_info where id in<foreach collection="ids" item="id" open="(" close=")" separator=",">#{id}</foreach>
</delete>
collection
:集合参数名item
:遍历元素别名open/close
:包裹符号separator
:分隔符
(5)<include>:SQL 片段复用
抽取重复 SQL 片段,提升可维护性:
<!-- 定义SQL片段 -->
<sql id="allColumns">id,book_name,author,count,price,publish,status,create_time,update_time
</sql><!-- 引用SQL片段 -->
<select id="queryAll" resultType="BookInfo">select <include refid="allColumns"/> from book_info
</select>
2.2 #{} 与 ${} 的核心区别
两者均用于参数注入,但存在本质差异:
特性 | #{} | ${} |
---|---|---|
处理方式 | 预编译 SQL,? 占位符 | 字符串直接替换 |
安全性 | 防止 SQL 注入 | 存在 SQL 注入风险 |
类型处理 | 自动添加引号 | 需手动添加引号 |
使用场景 | 普通参数注入 | 排序字段、表名等动态标识符 |
示例:排序功能实现排序字段无法使用#{}(会自动加引号导致语法错误)
,需使用${}
:
@Select("select * from book_info order by ${sortField} ${sortOrder}")
List<BookInfo> queryBySort(@Param("sortField") String sortField, @Param("sortOrder") String sortOrder);
注意:使用${}
时需严格校验参数,避免 SQL 注入。
2.3 分页实现
当数据量较大时,分页查询是必备功能。MyBatis 可通过LIMIT
关键字手动实现分页:
(1)分页请求与结果封装
// 分页请求参数
@Data
public class PageRequest {private int currentPage = 1; // 当前页码private int pageSize = 10; // 每页条数// 计算起始索引public int getOffset() {return (currentPage - 1) * pageSize;}
}// 分页结果封装
@Data
public class PageResult<T> {private int total; // 总记录数private List<T> records; // 当前页数据public PageResult(int total, List<T> records) {this.total = total;this.records = records;}
}
(2)分页查询实现
// Mapper接口
public interface BookInfoMapper {// 查询总记录数@Select("select count(1) from book_info where status<>0")Integer count();// 分页查询列表@Select("select * from book_info where status<>0 order by id desc limit #{offset},#{pageSize}")List<BookInfo> queryByPage(PageRequest pageRequest);
}// Service层
@Service
public class BookService {@Autowiredprivate BookInfoMapper bookMapper;public PageResult<BookInfo> getPage(PageRequest pageRequest) {int total = bookMapper.count();List<BookInfo> records = bookMapper.queryByPage(pageRequest);return new PageResult<>(total, records);}
}
2.4 数据库连接池
MyBatis 依赖数据库连接池管理连接,Spring Boot 默认使用 Hikari(性能优异),也可切换为 Druid(功能丰富)。
(1)切换为 Druid 连接池
引入依赖:
<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-3-starter</artifactId><version>1.2.21</version>
</dependency>
配置无需修改,Spring Boot 会自动识别并启用。
三、实战案例:图书管理系统核心功能
结合 Spring MVC 实现图书管理系统的核心功能,包含登录校验、图书 CRUD、分页查询等。
3.1 强制登录实现
通过 Session 校验用户登录状态,未登录则跳转至登录页:
// 常量定义
public class Constants {public static final String SESSION_USER_KEY = "session_user_key";
}// 登录接口
@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;@RequestMapping("/login")public boolean login(String username, String password, HttpSession session) {UserInfo user = userService.queryByUsername(username);if (user != null && password.equals(user.getPassword())) {user.setPassword(""); // 清空密码session.setAttribute(Constants.SESSION_USER_KEY, user);return true;}return false;}
}// 图书列表接口(登录校验)
@RestController
@RequestMapping("/book")
public class BookController {@Autowiredprivate BookService bookService;@RequestMapping("/getList")public Result<PageResult<BookInfo>> getList(PageRequest pageRequest, HttpSession session) {// 登录校验UserInfo user = (UserInfo) session.getAttribute(Constants.SESSION_USER_KEY);if (user == null) {return Result.unlogin(); // 未登录响应}PageResult<BookInfo> pageResult = bookService.getPage(pageRequest);return Result.success(pageResult);}
}
3.2 批量删除功能
// Mapper XML
<update id="batchDelete">update book_info set status=0 where id in<foreach collection="ids" item="id" open="(" close=")" separator=",">#{id}</foreach>
</update>// Controller
@RequestMapping("/batchDelete")
public boolean batchDelete(@RequestParam List<Integer> ids) {try {bookService.batchDelete(ids);return true;} catch (Exception e) {return false;}
}
四、MyBatis Generator:代码自动生成
MyBatis Generator(MBG)可根据数据库表结构自动生成实体类、Mapper 接口及 XML 文件,大幅提升开发效率。
4.1 配置 MBG 插件
在pom.xml
中添加插件:
<plugin><groupId>org.mybatis.generator</groupId><artifactId>mybatis-generator-maven-plugin</artifactId><version>1.3.6</version><configuration><configurationFile>src/main/resources/mybatis-generator/generatorConfig.xml</configurationFile><overwrite>true</overwrite></configuration><dependencies><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.33</version></dependency></dependencies>
</plugin>
4.2 编写配置文件
创建generatorConfig.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration PUBLIC"-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN""http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"><generatorConfiguration><context id="Mysql" targetRuntime="MyBatis3Simple"><!-- 禁用注释 --><commentGenerator><property name="suppressAllComments" value="true"/></commentGenerator><!-- 数据库连接 --><jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"connectionURL="jdbc:mysql://127.0.0.1:3306/book_test?serverTimezone=Asia/Shanghai"userId="root" password="root"></jdbcConnection><!-- 生成实体类 --><javaModelGenerator targetPackage="com.example.demo.model" targetProject="src/main/java"><property name="trimStrings" value="true"/></javaModelGenerator><!-- 生成XML文件 --><sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources"/><!-- 生成Mapper接口 --><javaClientGenerator targetPackage="com.example.demo.mapper" targetProject="src/main/java" type="XMLMAPPER"/><!-- 生成指定表 --><table tableName="book_info" domainObjectName="BookInfo"><generatedKey column="id" sqlStatement="Mysql" identity="true"/></table></context>
</generatorConfiguration>
4.3 生成代码
在 Maven 插件中执行mybatis-generator:generate
,即可自动生成实体类、Mapper 接口和 XML 文件。
五、总结与最佳实践
- 开发方式选择:简单 CRUD 用注解,复杂 SQL(动态 SQL、多表关联)用 XML。
- 参数注入:优先使用
#{}
防止 SQL 注入,仅在排序字段、表名等场景使用${}
并做好参数校验。 - 命名规范:数据库表 / 字段用蛇形命名(book_name),实体类用驼峰命名(bookName),开启
map-underscore-to-camel-case
自动转换。 - 代码复用:使用
<sql>
和<include>
抽取重复 SQL 片段,减少冗余。 - 性能优化:合理使用数据库连接池,避免
SELECT *
查询,分页查询减少数据传输量。
MyBatis 以其轻量、灵活的特性成为 Java 持久层框架的主流选择,掌握其基础用法与进阶特性,能显著提升数据库操作效率与代码质量。建议结合实际项目多做练习,深入理解其参数映射与动态 SQL 的实现原理。