一、核心概念
MyBatis 是一个优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。与 JPA 不同,MyBatis 更强调程序员对 SQL 的完全控制,将 SQL 语句从 Java 代码中分离出来,配置在 XML 文件或使用注解。
1. 核心组件
组件 | 作用 |
---|---|
@Mapper |
标记一个接口为 MyBatis 的 Mapper 接口。Spring Boot 会自动扫描并创建其实现。 |
@Repository |
可选,将 Mapper 接口注册为 Spring 的 Bean,便于事务管理。 |
Mapper 接口 (Interface) | 定义数据访问方法。方法名、参数、返回值与 SQL 映射。 |
SQL 映射文件 (XML) | .xml 文件,包含 <select> , <insert> , <update> , <delete> 等标签,定义 SQL 语句和结果映射。文件名通常与 Mapper 接口名相同。 |
@Select , @Insert , @Update , @Delete |
注解方式,直接在 Mapper 接口方法上编写 SQL 语句。适合简单查询。 |
@Results , @Result |
用于定义结果集到 Java 对象的映射关系,常与 @ResultMap 或 @Select 等一起使用。 |
@Param |
当 Mapper 方法有多个参数时,使用 @Param("name") 为参数命名,以便在 SQL 中引用。 |
SqlSessionFactory |
MyBatis 的核心工厂,负责创建 SqlSession 。Spring Boot 会自动配置。 |
SqlSession |
执行 SQL 命令、获取 Mapper 接口实例、管理事务。通常由 Spring 管理,开发者不直接操作。 |
MyBatis-Spring-Boot-Starter |
Spring Boot 官方提供的 Starter,简化了 MyBatis 在 Spring Boot 中的集成。 |
2. 工作原理
- 定义实体类:创建普通的 Java 对象 (POJO)。
- 定义 Mapper 接口:创建一个接口,方法代表数据库操作。
- 编写 SQL 映射:
- XML 方式:创建一个与接口同名的
.xml
文件,配置 SQL 和结果映射。 - 注解方式:直接在接口方法上使用
@Select
,@Insert
等注解。
- XML 方式:创建一个与接口同名的
- 自动扫描:Spring Boot 启动时,扫描
@Mapper
注解的接口。 - 动态代理:MyBatis 为每个 Mapper 接口生成一个动态代理实现。
- 依赖注入:在 Service 中通过
@Autowired
注入 Mapper 接口,调用其方法。 - 执行流程:调用 Mapper 方法 -> MyBatis 动态代理拦截 -> 根据方法名/注解找到对应 SQL -> 执行 SQL -> 处理结果集 -> 返回 Java 对象。
二、操作步骤(非常详细)
步骤 1:添加 MyBatis 依赖
在 pom.xml
(Maven) 或 build.gradle
(Gradle) 中添加 mybatis-spring-boot-starter
。
Maven (pom.xml
)
<dependencies>
<!-- MyBatis Spring Boot Starter -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version> <!-- 请检查最新版本 -->
</dependency>
<!-- 数据库驱动 (以 MySQL 8 为例) -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
Gradle (build.gradle
)
dependencies {
// MyBatis Spring Boot Starter
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.3'
// Database Driver (MySQL 8)
runtimeOnly 'mysql:mysql-connector-java'
}
步骤 2:配置数据源和 MyBatis (application.properties
)
在 src/main/resources/application.properties
中配置数据库连接和 MyBatis 相关设置。
# --- 数据源配置 ---
spring.datasource.url=jdbc:mysql://localhost:3306/mybatisdb?useSSL=false&serverTimezone=UTC
spring.datasource.username=myuser
spring.datasource.password=mypassword
# driver-class-name usually not needed
# --- MyBatis 配置 ---
# 指定 Mapper XML 文件的位置 (classpath* 搜索所有 jar 包)
# **XML 方式必须配置!**
mybatis.mapper-locations=classpath*:mapper/*.xml
# 指定实体类所在的包,用于别名注册 (可选,但推荐)
mybatis.type-aliases-package=com.example.demo.entity
# 开启驼峰命名自动映射 (数据库字段名如 user_name -> Java 属性名 userName)
mybatis.configuration.map-underscore-to-camel-case=true
# 开启 MyBatis 日志 (推荐使用 SLF4J)
mybatis.configuration.log-impl=org.apache.ibatis.logging.slf4j.Slf4jImpl
# 其他可选配置
# mybatis.configuration.cache-enabled=true # 是否开启二级缓存
# mybatis.configuration.lazy-loading-enabled=true # 是否开启延迟加载
# mybatis.configuration.aggressive-lazy-loading=false # 是否立即加载所有延迟加载的属性
步骤 3:创建实体类 (POJO)
创建一个普通的 Java 类,属性与数据库表字段对应。
// src/main/java/com/example/demo/entity/User.java
package com.example.demo.entity;
import java.time.LocalDateTime;
// 普通 Java 类,无需 JPA 注解
public class User {
private Long id;
private String firstName;
private String lastName;
private String email;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
// 无参构造函数
public User() {}
// 全参构造函数 (可选)
public User(String firstName, String lastName, String email) {
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}
// Getter 和 Setter 方法
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getFirstName() { return firstName; }
public void setFirstName(String firstName) { this.firstName = firstName; }
public String getLastName() { return lastName; }
public void setLastName(String lastName) { this.lastName = lastName; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public LocalDateTime getCreatedAt() { return createdAt; }
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
public LocalDateTime getUpdatedAt() { return updatedAt; }
public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }
// toString (推荐)
@Override
public String toString() {
return "User{" +
"id=" + id +
", firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", email='" + email + '\'' +
'}';
}
}
步骤 4:创建 Mapper 接口
创建一个接口,使用 @Mapper
注解。
方式 A:XML 映射文件 (推荐用于复杂 SQL)
// src/main/java/com/example/demo/mapper/UserMapper.java
package com.example.demo.mapper;
import com.example.demo.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
// 标记为 MyBatis Mapper
@Mapper
// 可选:注册为 Spring Repository Bean
@Repository
public interface UserMapper {
// 方法名必须与 XML 文件中的 id 一致
int insertUser(User user);
// 参数多于一个时,使用 @Param 指定名称
Optional<User> selectUserById(@Param("id") Long id);
List<User> selectUsersByLastName(@Param("lastName") String lastName);
List<User> selectAllUsers();
int updateUser(User user);
int deleteUserById(@Param("id") Long id);
// 自定义查询方法
List<User> searchUsers(@Param("keyword") String keyword);
}
方式 B:注解方式 (适合简单 SQL)
// src/main/java/com/example/demo/mapper/UserMapper.java
package com.example.demo.mapper;
import com.example.demo.entity.User;
import org.apache.ibatis.annotations.*;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
@Mapper
@Repository
public interface UserMapper {
// --- INSERT ---
@Insert("INSERT INTO users (first_name, last_name, email, created_at, updated_at) " +
"VALUES (#{firstName}, #{lastName}, #{email}, #{createdAt}, #{updatedAt})")
@Options(useGeneratedKeys = true, keyProperty = "id") // 获取自增主键
int insertUser(User user);
// --- SELECT ---
@Select("SELECT id, first_name, last_name, email, created_at, updated_at " +
"FROM users WHERE id = #{id}")
@Results({
@Result(property = "id", column = "id"),
@Result(property = "firstName", column = "first_name"),
@Result(property = "lastName", column = "last_name"),
@Result(property = "email", column = "email"),
@Result(property = "createdAt", column = "created_at"),
@Result(property = "updatedAt", column = "updated_at")
})
Optional<User> selectUserById(@Param("id") Long id);
@Select("SELECT id, first_name, last_name, email, created_at, updated_at " +
"FROM users WHERE last_name = #{lastName}")
List<User> selectUsersByLastName(@Param("lastName") String lastName);
@Select("SELECT id, first_name, last_name, email, created_at, updated_at FROM users")
List<User> selectAllUsers();
// --- UPDATE ---
@Update("UPDATE users SET first_name = #{firstName}, last_name = #{lastName}, " +
"email = #{email}, updated_at = #{updatedAt} WHERE id = #{id}")
int updateUser(User user);
// --- DELETE ---
@Delete("DELETE FROM users WHERE id = #{id}")
int deleteUserById(@Param("id") Long id);
}
步骤 5:创建 SQL 映射文件 (仅 XML 方式)
创建与 Mapper 接口同名的 .xml
文件,放在 src/main/resources/mapper/
目录下。
<!-- src/main/resources/mapper/UserMapper.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">
<!-- namespace 必须是 Mapper 接口的全限定名 -->
<mapper namespace="com.example.demo.mapper.UserMapper">
<!-- 通用的 resultMap (可选,用于复杂映射或复用) -->
<resultMap id="UserResultMap" type="User">
<id property="id" column="id"/>
<result property="firstName" column="first_name"/>
<result property="lastName" column="last_name"/>
<result property="email" column="email"/>
<result property="createdAt" column="created_at"/>
<result property="updatedAt" column="updated_at"/>
</resultMap>
<!-- INSERT -->
<insert id="insertUser" parameterType="User" useGeneratedKeys="true" keyProperty="id">
INSERT INTO users (
first_name,
last_name,
email,
created_at,
updated_at
) VALUES (
#{firstName},
#{lastName},
#{email},
#{createdAt},
#{updatedAt}
)
</insert>
<!-- SELECT -->
<!-- resultMap 指向上面定义的 resultMap -->
<select id="selectUserById" parameterType="long" resultMap="UserResultMap">
SELECT
id,
first_name,
last_name,
email,
created_at,
updated_at
FROM users
WHERE id = #{id}
</select>
<select id="selectUsersByLastName" parameterType="string" resultMap="UserResultMap">
SELECT
id,
first_name,
last_name,
email,
created_at,
updated_at
FROM users
WHERE last_name = #{lastName}
</select>
<select id="selectAllUsers" resultMap="UserResultMap">
SELECT
id,
first_name,
last_name,
email,
created_at,
updated_at
FROM users
</select>
<!-- 动态 SQL 示例:searchUsers -->
<select id="searchUsers" parameterType="string" resultMap="UserResultMap">
SELECT
id,
first_name,
last_name,
email,
created_at,
updated_at
FROM users
<where>
<if test="keyword != null and keyword != ''">
(first_name LIKE CONCAT('%', #{keyword}, '%')
OR last_name LIKE CONCAT('%', #{keyword}, '%')
OR email LIKE CONCAT('%', #{keyword}, '%'))
</if>
</where>
</select>
<!-- UPDATE -->
<update id="updateUser" parameterType="User">
UPDATE users
<set>
<if test="firstName != null">first_name = #{firstName},</if>
<if test="lastName != null">last_name = #{lastName},</if>
<if test="email != null">email = #{email},</if>
updated_at = #{updatedAt}
</set>
WHERE id = #{id}
</update>
<!-- DELETE -->
<delete id="deleteUserById" parameterType="long">
DELETE FROM users WHERE id = #{id}
</delete>
</mapper>
步骤 6:在 Service 或 Controller 中使用 Mapper
注入 UserMapper
并调用其方法。
// src/main/java/com/example/demo/service/UserService.java
package com.example.demo.service;
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
@Service
@Transactional // 为数据修改操作添加事务管理
public class UserService {
@Autowired
private UserMapper userMapper; // 注入 Mapper
public User createUser(User user) {
LocalDateTime now = LocalDateTime.now();
user.setCreatedAt(now);
user.setUpdatedAt(now);
userMapper.insertUser(user); // 执行插入
// useGeneratedKeys=true 会自动将生成的主键设置回 user 对象
return user;
}
public Optional<User> getUserById(Long id) {
return userMapper.selectUserById(id);
}
public List<User> getAllUsers() {
return userMapper.selectAllUsers();
}
public List<User> getUsersByLastName(String lastName) {
return userMapper.selectUsersByLastName(lastName);
}
public List<User> searchUsers(String keyword) {
return userMapper.searchUsers(keyword); // 仅 XML 方式有此方法
}
public User updateUser(User user) {
user.setUpdatedAt(LocalDateTime.now());
userMapper.updateUser(user);
return user;
}
public void deleteUser(Long id) {
userMapper.deleteUserById(id);
}
}
// src/main/java/com/example/demo/controller/UserController.java
package com.example.demo.controller;
import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Optional;
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
User createdUser = userService.createUser(user);
return ResponseEntity.ok(createdUser);
}
@GetMapping("/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
Optional<User> userOpt = userService.getUserById(id);
return userOpt.map(ResponseEntity::ok)
.orElseGet(() -> ResponseEntity.notFound().build());
}
@GetMapping
public ResponseEntity<List<User>> getAllUsers() {
List<User> users = userService.getAllUsers();
return ResponseEntity.ok(users);
}
@GetMapping("/search")
public ResponseEntity<List<User>> searchUsers(@RequestParam String keyword) {
List<User> users = userService.searchUsers(keyword);
return ResponseEntity.ok(users);
}
@PutMapping("/{id}")
public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User user) {
// 确保 ID 一致
user.setId(id);
User updatedUser = userService.updateUser(user);
return ResponseEntity.ok(updatedUser);
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
userService.deleteUser(id);
return ResponseEntity.noContent().build();
}
}
步骤 7:启动应用并测试
- 启动 Spring Boot 应用。
- 确保数据库
mybatisdb
存在,并创建了users
表(可手动创建或使用其他工具)。CREATE TABLE users ( id BIGINT AUTO_INCREMENT PRIMARY KEY, first_name VARCHAR(50) NOT NULL, last_name VARCHAR(50) NOT NULL, email VARCHAR(100) UNIQUE NOT NULL, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL );
- 使用 Postman, curl 或浏览器测试 API 端点。
三、常见错误与解决方案
错误 | 原因 | 解决方案 |
---|---|---|
Invalid bound statement (not found): com.example.demo.mapper.UserMapper.selectUserById |
Mapper XML 文件未被正确加载或 namespace /id 不匹配 |
1. 检查 application.properties 中 mybatis.mapper-locations 路径是否正确(如 classpath*:mapper/*.xml )。2. 确认 XML 文件在 src/main/resources 下。3. 检查 XML 中的 namespace 是否是 Mapper 接口的全限定名。4. 检查 <select> 等标签的 id 是否与接口方法名完全一致。 |
BindingException: Parameter 'xxx' not found |
Mapper 方法参数未用 @Param 注解,但在 SQL 中引用了 |
当 Mapper 方法有多个参数时,必须使用 @Param("name") 为每个参数命名。单个参数(如 POJO)通常不需要。 |
org.apache.ibatis.reflection.ReflectionException: Could not set property 'xxx' of 'class com.example.demo.entity.User' |
实体类缺少 setter 方法或属性名不匹配 | 1. 确保实体类为需要映射的字段提供了 public 的 setter 方法。2. 检查数据库列名与 Java 属性名是否匹配(考虑驼峰命名转换 map-underscore-to-camel-case )。3. 在 resultMap 或 @Results 中明确指定 property 和 column 的映射。 |
org.apache.ibatis.binding.BindingException: Invalid return type for method |
SQL 查询返回的列数/类型与返回的 Java 对象不匹配 | 1. 确保 SELECT 语句返回了所有需要的列。2. 检查 resultMap 或 @Results 定义是否正确。3. 对于 Optional<T> ,确保查询可能返回 null (如 SELECT ... WHERE id = ? )。 |
java.sql.SQLIntegrityConstraintViolationException |
违反了数据库约束 (唯一键、非空等) | 检查插入/更新的数据是否符合数据库表的约束。在业务逻辑中提前验证(如检查邮箱是否已存在)。 |
NoSuchBeanDefinitionException: No qualifying bean of type 'YourMapper' |
Mapper 接口未被 Spring 扫描到 | 1. 确保 Mapper 接口上有 @Mapper 注解。2. 确保主应用类(@SpringBootApplication )的包能扫描到 Mapper 接口(通常在子包下)。3. 或者,在主应用类上使用 @MapperScan("com.example.demo.mapper") 注解指定扫描包。 |
四、注意事项
@Mapper
vs@MapperScan
:@Mapper
:需要在每个 Mapper 接口上添加。@MapperScan
:在主应用类或配置类上使用一次,指定 Mapper 接口所在的包,Spring Boot 会自动扫描该包下所有接口。
@SpringBootApplication @MapperScan("com.example.demo.mapper") // 推荐方式 public class Application { ... }
@Param
的使用:当方法有多个参数时,必须使用@Param
。单个参数(如 POJO 或基本类型)通常可以省略,但显式使用@Param
更清晰。- SQL 注入风险:MyBatis 使用
#{}
进行参数占位,能有效防止 SQL 注入。避免使用${}
,除非你完全信任输入且需要动态表名/列名。 useGeneratedKeys
&keyProperty
:对于自增主键,<insert>
标签或@Insert
注解需要设置useGeneratedKeys="true"
和keyProperty="id"
(id
是实体类中主键属性名),才能将生成的主键值回填到实体对象。resultMap
vsresultType
:resultType
:当数据库列名与 Java 属性名能自动匹配(如开启驼峰转换)时使用,更简洁。resultMap
:当需要复杂映射(如关联查询、列名与属性名不一致、类型转换)时使用,更灵活。
@Transactional
:对数据修改操作(insert
,update
,delete
)强烈建议使用@Transactional
注解,确保操作的原子性。Optional<T>
:MyBatis 支持返回Optional<T>
,当查询结果为空时返回Optional.empty()
。注意,List<T>
永远不会是null
,空结果返回空List
。
五、使用技巧
- 动态 SQL:MyBatis 的强大之处在于动态 SQL。熟练使用
<if>
,<choose>
,<when>
,<otherwise>
,<where>
,<set>
,<foreach>
等标签构建灵活的查询。<where>
:自动处理WHERE
关键字和多余的AND
/OR
。<set>
:自动处理SET
关键字和多余的逗号。<foreach>
:用于IN
子句或批量操作。
<sql>
片段:抽取可重用的 SQL 片段。<sql id="baseColumns"> id, first_name, last_name, email, created_at, updated_at </sql> <select id="selectAllUsers" resultMap="UserResultMap"> SELECT <include refid="baseColumns"/> FROM users </select>
<association>
和<collection>
:处理一对一、一对多关联映射。@Options
注解:用于@Insert
,@Update
等,设置useGeneratedKeys
,keyProperty
,timeout
等。@ResultMap
:在方法上引用已定义的resultMap
。@Select("SELECT * FROM users") @ResultMap("UserResultMap") List<User> selectAllUsers();
@SelectProvider
,@InsertProvider
等:使用 Java 代码动态生成 SQL 语句。@UpdateProvider
:配合@Param
和Map
实现更灵活的更新逻辑。RowBounds
:用于分页(逻辑分页,不推荐用于大数据量)。List<User> selectAllUsers(RowBounds rowBounds);
@Flush
:强制刷新所有待执行的语句到数据库。
六、最佳实践
- 选择 XML 还是注解:
- XML 方式:推荐。SQL 与代码分离,便于维护、格式化、版本控制,支持复杂的动态 SQL 和
resultMap
。 - 注解方式:适合非常简单的 CRUD 操作。SQL 写在 Java 代码中,不利于复杂 SQL 的管理和阅读。
- XML 方式:推荐。SQL 与代码分离,便于维护、格式化、版本控制,支持复杂的动态 SQL 和
- 使用
@MapperScan
:避免在每个 Mapper 接口上写@Mapper
。 - 分层架构:Controller -> Service -> Mapper。Mapper 只负责数据访问,Service 负责业务逻辑和事务。
- 事务管理:在 Service 层 使用
@Transactional
。 - 使用
#{}
:始终使用#{parameter}
进行参数绑定,防止 SQL 注入。${}
仅在极少数需要动态 SQL 片段时使用,并确保输入安全。 - 合理使用动态 SQL:利用 MyBatis 的动态标签构建安全、高效的查询。
- DTO 模式:在 Controller 层和前端之间使用 DTO,避免直接暴露实体类。
- 日志配置:开启 MyBatis 日志(
log-impl=SLF4J
),便于调试 SQL 语句和参数。 map-underscore-to-camel-case
:开启驼峰命名转换,减少resultMap
的定义。- 主键回填:对于自增主键,务必配置
useGeneratedKeys
和keyProperty
。
七、性能优化
- 避免 N+1 查询:虽然 MyBatis 默认是
EAGER
,但复杂关联仍需注意。使用<association>
和<collection>
的select
属性(延迟加载)或JOIN
查询一次性获取数据。谨慎使用延迟加载。 - 使用二级缓存 (2nd Level Cache):MyBatis 支持二级缓存(基于
namespace
)。在 Mapper XML 中使用<cache/>
标签开启。注意缓存一致性问题,在高并发写场景下可能不适用。<mapper namespace="com.example.demo.mapper.UserMapper"> <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/> ... </mapper>
- 合理使用
fetchType
:在<association>
和<collection>
中设置fetchType="lazy"
实现延迟加载。 - 批量操作:
- 批量插入:使用
<foreach>
构建INSERT INTO ... VALUES (...), (...), (...)
语句。<insert id="batchInsertUsers"> INSERT INTO users (first_name, last_name, email, created_at, updated_at) VALUES <foreach collection="users" item="user" separator=","> (#{user.firstName}, #{user.lastName}, #{user.email}, #{user.createdAt}, #{user.updatedAt}) </foreach> </insert>
- 批量更新/删除:类似地使用
<foreach>
。
- 批量插入:使用
- 连接池优化:确保底层数据源(如 HikariCP)配置合理(最大连接数、最小空闲连接、连接超时等)。
- SQL 优化:编写高效的 SQL 语句,为查询条件字段添加数据库索引。
- 减少查询字段:只
SELECT
需要的字段,避免SELECT *
。 - 分页:对于大数据量查询,必须使用物理分页。可以使用 MyBatis-Plus 的分页插件,或在 SQL 中使用
LIMIT
/ROWNUM
/OFFSET FETCH
等数据库特定语法。
总结
MyBatis 提供了对 SQL 的完全控制,适合需要编写复杂、高性能 SQL 的场景。
快速掌握路径:
- 添加依赖:
mybatis-spring-boot-starter
+ 数据库驱动。 - 配置:
application.properties
中设置数据源和mybatis.mapper-locations
,mybatis.type-aliases-package
,mybatis.configuration.map-underscore-to-camel-case
。 - 创建实体:普通的 POJO。
- 创建 Mapper 接口:使用
@Mapper
或@MapperScan
。 - 编写 SQL:
- 推荐 XML:创建
.xml
文件,配置 SQL 和resultMap
。 - 简单 SQL:使用
@Select
,@Insert
等注解。
- 推荐 XML:创建
- 注入 Mapper:在 Service 中
@Autowired
并调用方法。 - 添加
@Transactional
:在 Service 的修改方法上。 - 测试:通过 API 验证功能。
遵循最佳实践,特别是使用 XML 方式、合理使用动态 SQL、开启驼峰转换、注意事务和性能,你就能高效地使用 MyBatis 进行数据访问。