Spring Boot 集成 MyBatis 的核心方案与自动化开发实践
环境准备与核心依赖
在 Spring Boot 项目中引入 MyBatis 需要配置持久层 starter、数据库驱动以及必要的工具类。推荐使用 Lombok 简化实体类样板代码,并引入热部署插件提升开发效率。
<dependencies>
<!-- Web 基础环境 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MyBatis 集成 Starter -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.0</version>
</dependency>
<!-- MySQL 连接器 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 代码简化与热更新 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
全局配置文件设计
通过 YAML 格式集中管理应用参数。开启驼峰命名自动映射、配置数据源信息,并指定 MyBatis 的映射文件路径(XML 方案时生效)。
server:
port: 8080
servlet:
context-path: /api
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/demo_db?useSSL=false&serverTimezone=Asia/Shanghai
username: db_admin
password: secure_pwd
driver-class-name: com.mysql.cj.jdbc.Driver
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
mybatis:
mapper-locations: classpath:/mappers/**/*.xml
config-location: classpath:/mybatis-config.xml
configuration:
map-underscore-to-camel-case: true
基础模型与响应封装
统一接口返回结构有助于前端标准化处理。结合 Lombok 注解可大幅减少 Getter/Setter 代码量。
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ApiResponse<T> {
private int code;
private String msg;
private T payload;
public static ApiResponse success(T data) {
return new ApiResponse(200, "执行成功", data);
}
public static ApiResponse fail(String reason) {
return new ApiResponse(400, reason, null);
}
}
@Data
public class Member {
private Integer id;
private String account;
private String credential;
private LocalDateTime createdAt;
}
数据表结构初始化
CREATE TABLE `sys_member` (
`id` INT(11) NOT NULL AUTO_INCREMENT COMMENT '主键标识',
`account` VARCHAR(64) DEFAULT NULL COMMENT '登录账号',
`credential` VARCHAR(255) DEFAULT NULL COMMENT '加密凭证',
`created_at` DATETIME DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='会员基础信息表';
控制层与业务层架构
遵循分层原则,Controller 仅负责参数接收与响应封装,Service 处理业务逻辑与事务边界。
@RestController
@RequestMapping("/members")
public class MemberEndpoint {
@Autowired
private MemberManager memberService;
@PostMapping("/save")
public ApiResponse save(@RequestBody Member entity) {
return memberService.persist(entity);
}
@GetMapping("/find")
public ApiResponse query(@RequestParam(required = false) Integer id) {
Member criteria = new Member();
criteria.setId(id);
return memberService.locate(criteria);
}
}
@Service
public class MemberManager {
@Autowired
private MemberRepository memberDao;
public ApiResponse persist(Member entity) {
int affected = memberDao.insert(entity);
return affected > 0 ? ApiResponse.success(entity) : ApiResponse.fail("写入失败");
}
public ApiResponse locate(Member criteria) {
Member result = memberDao.select(criteria);
return result != null ? ApiResponse.success(result) : ApiResponse.fail("未找到记录");
}
}
数据访问层实现方案
方案一:注解驱动模式
将 SQL 语句直接声明在接口方法上,适合简单查询。需注意参数绑定符的差异:`#{}` 会进行预编译防注入,适用于值传递;`${}` 为纯字符串拼接,适用于动态表名或字段名排序。
@Mapper
public interface MemberRepository {
@Insert("INSERT INTO sys_member(account, credential, created_at) VALUES(#{account}, #{credential}, #{createdAt})")
@Options(useGeneratedKeys = true, keyProperty = "id")
int insert(Member entity);
@Select("SELECT * FROM sys_member WHERE id = #{id}")
Member select(Member criteria);
@Delete("DELETE FROM sys_member WHERE id = #{id}")
int delete(Member criteria);
}
方案二:XML 映射模式
将 SQL 剥离至独立配置文件,便于复杂查询维护。接口层仅保留方法签名。
@Mapper
public interface MemberRepository {
int insert(Member entity);
Member select(Member criteria);
int delete(Member criteria);
}
<?xml version="1.0" encoding="UTF-8" ?>
<mapper namespace="com.example.dao.MemberRepository">
<insert id="insert" useGeneratedKeys="true" keyProperty="id" parameterType="com.example.model.Member">
INSERT INTO sys_member(account, credential, created_at)
VALUES(#{account}, #{credential}, #{createdAt})
</insert>
<select id="select" parameterType="com.example.model.Member" resultType="com.example.model.Member">
SELECT id, account, credential, created_at
FROM sys_member
WHERE id = #{id}
</select>
<delete id="delete" parameterType="com.example.model.Member">
DELETE FROM sys_member WHERE id = #{id}
</delete>
</mapper>
逆向工程自动化生成
针对多表系统,手动编写 CRUD 效率较低。MyBatis Generator (MBG) 可根据数据库表结构自动生成实体类、Mapper 接口及 XML 映射文件。核心在于编写生成器配置文件并执行驱动类。
<generatorConfiguration>
<context id="MyBatisGenerator" targetRuntime="MyBatis3">
<commentGenerator>
<property name="suppressAllComments" value="true"/>
</commentGenerator>
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://127.0.0.1:3306/demo_db"
userId="db_admin" password="secure_pwd"/>
<javaModelGenerator targetPackage="com.example.model" targetProject="./src/main/java">
<property name="trimStrings" value="true"/>
</javaModelGenerator>
<sqlMapGenerator targetPackage="mappers" targetProject="./src/main/resources"/>
<javaClientGenerator type="XMLMAPPER" targetPackage="com.example.dao" targetProject="./src/main/java"/>
<table tableName="sys_member" domainObjectName="Member"/>
</context>
</generatorConfiguration>
public class GeneratorRunner {
public static void main(String[] args) throws Exception {
List<String> warnings = new ArrayList<>();
File configFile = new File("src/main/resources/generatorConfig.xml");
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(configFile);
DefaultShellCallback callback = new DefaultShellCallback(true);
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
myBatisGenerator.generate(null);
System.out.println("代码生成完毕,请检查目标目录。");
}
}
执行后,框架会自动处理字段命名转换:数据库下划线格式(如 `created_at`)将映射为驼峰属性(`createdAt`),并生成配套的 `Example` 查询构建器,支持链式条件拼装。
集成 PageHelper 实现分页
物理分页是生产环境的刚需。PageHelper 通过拦截器机制在目标 SQL 后自动追加 `LIMIT` 语句,支持主流关系型数据库。
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.7</version>
</dependency>
在 `mybatis-config.xml` 中声明拦截器插件:
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<property name="helperDialect" value="mysql"/>
<property name="reasonable" value="true"/>
</plugin>
</plugins>
</configuration>
业务调用示例:
// 启动分页,参数分别为页码与每页大小
PageHelper.startPage(currentPage, pageSize);
// 仅拦截紧随其后的第一条 SELECT 查询
List<Member> records = memberDao.select(criteria);
// 封装分页元数据
PageInfo<Member> pageData = new PageInfo<>(records);
底层原理依赖于 `ThreadLocal` 实现线程隔离,拦截器获取当前线程的 SQL 语句后动态重写为带分页参数的语句,处理完毕后再清理上下文。需注意:分页指令仅对紧接 `startPage` 的查询生效,且不支持 `FOR UPDATE` 或复杂嵌套关联查询的分页场景。