Spring Boot + MyBatis
一、MyBatis 注解
1. MyBatis 是什么?
MyBatis 是一款优秀的 持久层框架,作用是:
- 将 SQL 从 Java 代码中分离,写在 XML 或注解中,便于维护。
- 自动将 SQL 查询结果映射为 Java 对象。
- 支持动态 SQL、分页、缓存等功能。
相比 JPA(Hibernate),MyBatis 更灵活,SQL 可控性强,适合对性能和 SQL 结构有要求的项目。
2. 准备环境
技术栈:
- JDK 17(JDK 8 以上均可)
- Spring Boot 3.x
- MyBatis-Spring-Boot-Starter
- MySQL 8.x
- Maven
3. 创建数据库表
以 user
表为例:
CREATE DATABASE mybatis_demo DEFAULT CHARSET utf8mb4;
USE mybatis_demo;
CREATE TABLE user (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updataed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
注意:
updataed_at
是保持与你的字段名一致,实际中推荐使用updated_at
。
4. 新建 Spring Boot 项目依赖
pom.xml
添加如下依赖:
<dependencies>
<!-- 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>
<version>8.3.0</version>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
<scope>provided</scope>
</dependency>
</dependencies>
5. 配置数据库与 MyBatis
好的,帮你把简短的 URL 各部分说明加到你的配置说明里,整合如下:
spring:
datasource:
url: jdbc:mysql://localhost:3306/mybatis_demo?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: root
mybatis:
type-aliases-package: com.example.demo.entity
configuration:
map-underscore-to-camel-case: true # 开启驼峰映射
spring.datasource 配置数据库连接信息:
url
:数据库地址和连接参数jdbc:mysql://
:使用 JDBC 协议连接 MySQLlocalhost
:数据库服务器地址(本机)3306
:MySQL 默认端口号mybatis_demo
:连接的数据库名characterEncoding=utf8
:设置字符编码为 UTF-8,防止乱码useSSL=false
:关闭 SSL 连接serverTimezone=Asia/Shanghai
:设置时区为上海,避免时间偏差
username
:数据库用户名password
:数据库密码
mybatis.type-aliases-package
指定实体类所在包,MyBatis 自动为包内类生成别名,简化 SQL 映射配置。mybatis.configuration.map-underscore-to-camel-case
开启数据库字段下划线到 Java 驼峰命名的自动映射,如created_at
自动映射为createdAt
,减少手动配置。
6. 创建实体类
@Data
来自 Lombok,自动生成 getter/setter、toString、equals、hashCode 等,减少样板代码。
package com.example.demo.entity;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class User {
private Integer id;
private String username;
private LocalDateTime createdAt;
private LocalDateTime updataedAt; // 保持与数据库字段名一致
}
7. 编写 Mapper 接口
7.1 @Mapper
标记接口为 MyBatis Mapper,Spring Boot 自动扫描并生成代理。
package com.example.demo.mapper;
import com.example.demo.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface UserMapper {
@Select("SELECT id, username FROM user")
List<User> findAll();
}
7.2 注解方式的 CRUD
@Select
:查询,返回实体或集合@Insert
:插入,参数为对象,返回受影响行数@Update
:更新,返回受影响行数@Delete
:删除,返回受影响行数
参数绑定
- 使用
#{参数名}
绑定方法参数或对象属性 - 基础类型参数直接绑定变量名,对象参数绑定属性名
示例:
@Mapper
public interface UserMapper {
@Select("SELECT id, username FROM user")
List<User> findAll();
@Select("SELECT id, username FROM user WHERE id = #{id}")
User findById(int id);
@Insert("INSERT INTO user (username) VALUES (#{username})")
@Options(useGeneratedKeys = true, keyProperty = "id")
int insert(User user);
@Update("UPDATE user SET username=#{username} WHERE id=#{id}")
int update(User user);
@Delete("DELETE FROM user WHERE id = #{id}")
int delete(int id);
}
@Options(useGeneratedKeys = true, keyProperty = "id")
会将数据库自动生成的主键回写到 User 实体的id
字段。
8. 业务层 Service
封装调用 Mapper,方便扩展业务逻辑:
package com.example.demo.service;
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
@Service
public class UserService {
@Resource
private UserMapper userMapper;
public List<User> getAllUsers() {
return userMapper.findAll();
}
public User getUserById(int id) {
return userMapper.findById(id);
}
public int addUser(User user) {
return userMapper.insert(user);
}
public int updateUser(User user) {
return userMapper.update(user);
}
public int deleteUser(int id) {
return userMapper.delete(id);
}
}
9. 控制层 Controller
接收 HTTP 请求,调用 Service,返回 JSON 结果或操作提示。
package com.example.demo.controller;
import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
@Slf4j
@RestController
@RequestMapping("/t")
public class TetsController {
@Autowired
private UserService userService;
@GetMapping("/getAllUsers")
public List<User> getAllUsers() {
log.info("请求获取所有用户");
return userService.getAllUsers();
}
@GetMapping("/getUserById/{id}")
public User getUserById(@PathVariable int id) {
log.info("请求获取用户,id={}", id);
return userService.getUserById(id);
}
@PostMapping("/addUser")
public String addUser(@RequestBody User user) {
log.info("请求添加用户: {}", user);
int result = userService.addUser(user);
return result > 0 ? "添加用户成功" : "添加用户失败";
}
@PutMapping("/updateUser")
public String updateUser(@RequestBody User user) {
log.info("请求更新用户: {}", user);
int result = userService.updateUser(user);
return result > 0 ? "更新用户成功" : "更新用户失败";
}
@DeleteMapping("/deleteUser/{id}")
public String deleteUser(@PathVariable int id) {
log.info("请求删除用户,id={}", id);
int result = userService.deleteUser(id);
return result > 0 ? "删除用户成功" : "删除用户失败";
}
}
10. 总结
- 使用
@Mapper
注解标记 Mapper 接口,简化开发流程。 - 注解方式定义 SQL,免去 XML 配置,开发更快捷。
- 通过
@Options
实现主键自动回写,方便获取插入记录的 ID。 - 业务层封装 Mapper 操作,易于扩展。
- 控制层处理 HTTP 请求,结合日志打印,便于调试和维护。
二、MyBatis XML
1. 为什么要用 XML 映射文件
虽然注解方式简单,但对于以下情况,XML 方式更适合:
- SQL 较复杂(多表关联、动态 SQL、分页)。
- 需要维护大量 SQL,集中管理更方便。
- 便于 DBA 直接修改 SQL,不影响 Java 代码。
2. 数据库表结构
使用前面教程的 user
表:
CREATE DATABASE mybatis_demo DEFAULT CHARSET utf8mb4;
USE mybatis_demo;
CREATE TABLE user (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
(这里我把 updataed_at
改成了 updated_at
)
3. Maven 依赖
pom.xml
跟注解版相同:
<dependencies>
<!-- 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>
<version>8.3.0</version>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
<scope>provided</scope>
</dependency>
</dependencies>
4. Spring Boot 配置
application.yml
:
spring:
datasource:
url: jdbc:mysql://localhost:3306/mybatis_demo?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: root
mybatis:
type-aliases-package: com.example.demo.entity # 别名包
mapper-locations: classpath:mapper/*.xml # XML 文件位置
configuration:
map-underscore-to-camel-case: true # 下划线转驼峰
5. 实体类
com/example/demo/entity/User.java
package com.example.demo.entity;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class User {
private Integer id;
private String username;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
6. Mapper 接口
com/example/demo/mapper/UserMapper.java
package com.example.demo.mapper;
import com.example.demo.entity.User;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface UserMapper {
List<User> findAll();
User findById(int id);
int insert(User user);
int update(User user);
int delete(int id);
}
7. XML 映射文件
MyBatis XML 文件扫描方式说明
MyBatis 在运行时需要加载 .xml
映射文件(Mapper XML),常用的两种方式如下:
方式一:通过 mapper-locations
配置(推荐)
在 application.yml
或 application.properties
中显式指定扫描路径:
mybatis:
mapper-locations: classpath:mapper/*.xml
classpath:
表示src/main/resources/
下的路径。mapper/*.xml
表示扫描resources/mapper/
目录下的所有.xml
文件。- 优点:XML 文件集中管理在
mapper
目录,结构清晰。
路径结构示例:
src
├─ main
│ ├─ java
│ │ └─ com/example/demo/mapper/UserMapper.java
│ └─ resources
│ └─ mapper/UserMapper.xml
方式二:与 Mapper 接口同路径同名(自动匹配)
- 将
.xml
文件放在与Mapper 接口
相同的包路径下,并与接口同名。 - MyBatis 会根据 Mapper 接口的全类名自动去匹配对应的 XML 文件。
- 优点:不需要在
application.yml
里配置mapper-locations
。
路径结构示例:
src
├─ main
│ ├─ java
│ │ └─ com/example/demo/mapper/UserMapper.java
│ └─ resources
│ └─ com/example/demo/mapper/UserMapper.xml
注意事项
namespace
必须与 Mapper 接口全类名一致
例如:<mapper namespace="com.example.demo.mapper.UserMapper">
SQL 标签
<select>
、<insert>
、<update>
、<delete>
的id
必须和接口方法名一致。如果不配置
mapper-locations
,但路径又没和接口保持一致,MyBatis 就找不到 XML 文件,会报错:org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)
这是 MyBatis Mapper XML 文件的 DOCTYPE声明,作用是告诉解析器这个 XML 文件遵循 MyBatis 3.0 的规范,帮助验证文件结构和语法正确性。
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
mapper
是根元素名- 指定了官方托管的 DTD 文件地址
- 有助于IDE校验和智能提示
src/main/resources/mapper/UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper">
<!-- namespace 指定当前 XML 映射文件对应的 Mapper 接口的全限定名,
MyBatis 根据它来绑定接口方法和 XML 语句 -->
<!-- 查询全部 -->
<select id="findAll" resultType="User">
<!-- id 是方法名,MyBatis 会绑定接口 UserMapper 的 findAll() 方法调用此 SQL -->
<!-- resultType 指定返回的 Java 类型,这里返回 User 实体类 -->
SELECT id, username, created_at, updated_at
FROM user
</select>
<!-- 根据 ID 查询 -->
<select id="findById" parameterType="int" resultType="User">
<!-- parameterType 指定传入参数类型,这里是基本类型 int -->
SELECT id, username, created_at, updated_at
FROM user
WHERE id = #{id}
<!-- #{id} 是参数占位符,MyBatis 会将接口方法参数传入此处 -->
</select>
<!-- 插入用户 -->
<insert id="insert" parameterType="User" useGeneratedKeys="true" keyProperty="id">
<!-- useGeneratedKeys = true 表示使用 JDBC 的 getGeneratedKeys 方法自动获取数据库生成的主键 -->
<!-- keyProperty = "id" 指定将主键回写到 User 对象的 id 属性 -->
INSERT INTO user (username)
VALUES (#{username})
</insert>
<!-- 更新用户 -->
<update id="update" parameterType="User">
UPDATE user
SET username = #{username}
WHERE id = #{id}
</update>
<!-- 删除用户 -->
<delete id="delete" parameterType="int">
DELETE FROM user WHERE id = #{id}
</delete>
</mapper>
8. Service 层
跟注解版一致:
package com.example.demo.service;
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public List<User> getAllUsers() {
return userMapper.findAll();
}
public User getUserById(int id) {
return userMapper.findById(id);
}
public int addUser(User user) {
return userMapper.insert(user);
}
public int updateUser(User user) {
return userMapper.update(user);
}
public int deleteUser(int id) {
return userMapper.delete(id);
}
}
9. Controller 层
跟注解版一致:
package com.example.demo.controller;
import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
@Slf4j
@RestController
@RequestMapping("/t")
public class TestController {
@Autowired
private UserService userService;
@GetMapping("/getAllUsers")
public List<User> getAllUsers() {
return userService.getAllUsers();
}
@GetMapping("/getUserById/{id}")
public User getUserById(@PathVariable int id) {
return userService.getUserById(id);
}
@PostMapping("/addUser")
public String addUser(@RequestBody User user) {
int result = userService.addUser(user);
return result > 0 ? "添加用户成功" : "添加用户失败";
}
@PutMapping("/updateUser")
public String updateUser(@RequestBody User user) {
int result = userService.updateUser(user);
return result > 0 ? "更新用户成功" : "更新用户失败";
}
@DeleteMapping("/deleteUser/{id}")
public String deleteUser(@PathVariable int id) {
int result = userService.deleteUser(id);
return result > 0 ? "删除用户成功" : "删除用户失败";
}
}
10. 总结
- XML 文件方式更适合复杂的 SQL 管理和动态 SQL
- 通过
mapper-locations
配置扫描 XML 文件更灵活,结构清晰 - XML 中
namespace
和接口必须严格对应 - SQL 语句写法标准,可复用性好,便于维护
- 注解方式适合简单场景,XML 方式适合复杂业务
- 配合 Spring Boot,MyBatis 代码简洁高效,开发友好