MyBatis 的 新增、修改、删除 操作
一、准备工作回顾
- 项目类型:Spring Boot 非 Web 应用
- 数据库:MySQL(表结构沿用前文的
user表) - 映射方式:XML 文件(更灵活,支持复杂 SQL)
- 测试方式:
CommandLineRunner或单元测试
表结构(简化版):
CREATE TABLE user (id INT PRIMARY KEY AUTO_INCREMENT,name VARCHAR(50),email VARCHAR(100)
);二、实体类与 Mapper 接口
1. User 实体类
// src/main/java/com/example/mybatisdemo/model/User.java
package com.example.mybatisdemo.model;public class User {private Integer id;private String name;private String email;// 构造函数(可选)public User() {}public User(String name, String email) {this.name = name;this.email = email;}// Getters & Setterspublic Integer getId() { return id; }public void setId(Integer id) { this.id = id; }public String getName() { return name; }public void setName(String name) { this.name = name; }public String getEmail() { return email; }public void setEmail(String email) { this.email = email; }@Overridepublic String toString() {return "User{id=" + id + ", name='" + name + "', email='" + email + "'}";}
}2. UserMapper 接口(定义 CRUD 方法)
// src/main/java/com/example/mybatisdemo/mapper/UserMapper.java
package com.example.mybatisdemo.mapper;import com.example.mybatisdemo.model.User;
import org.apache.ibatis.annotations.Mapper;import java.util.List;@Mapper
public interface UserMapper {// 查询List<User> findAll();User findById(Integer id);// 新增int insert(User user); // 普通插入int insertSelective(User user); // 选择性插入(非空字段)// 修改int updateById(User user); // 全量更新int updateSelectiveById(User user); // 选择性更新// 删除int deleteById(Integer id);int deleteByIds(List<Integer> ids); // 批量删除
}三、XML 映射文件详解(UserMapper.xml)
路径: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"><mapper namespace="com.example.mybatisdemo.mapper.UserMapper"><!-- 查询 --><select id="findAll" resultType="com.example.mybatisdemo.model.User">SELECT id, name, email FROM user</select><select id="findById" resultType="User" parameterType="int">SELECT id, name, email FROM user WHERE id = #{id}</select><!-- 1. 新增:全字段插入 --><insert id="insert" parameterType="User" useGeneratedKeys="true" keyProperty="id">INSERT INTO user (name, email)VALUES (#{name}, #{email})</insert><!-- 2. 新增:选择性插入(仅插入非 null 字段) --><insert id="insertSelective" parameterType="User" useGeneratedKeys="true" keyProperty="id">INSERT INTO user<trim prefix="(" suffix=")" suffixOverrides=","><if test="name != null">name,</if><if test="email != null">email,</if></trim><trim prefix="VALUES (" suffix=")" suffixOverrides=","><if test="name != null">#{name},</if><if test="email != null">#{email},</if></trim></insert><!-- 3. 修改:全量更新 --><update id="updateById" parameterType="User">UPDATE userSET name = #{name}, email = #{email}WHERE id = #{id}</update><!-- 4. 修改:选择性更新(只更新非 null 字段) --><update id="updateSelectiveById" parameterType="User">UPDATE user<set><if test="name != null">name = #{name},</if><if test="email != null">email = #{email},</if></set>WHERE id = #{id}</update><!-- 5. 删除:单条 --><delete id="deleteById" parameterType="int">DELETE FROM user WHERE id = #{id}</delete><!-- 6. 删除:批量(使用 foreach) --><delete id="deleteByIds" parameterType="java.util.List">DELETE FROM user WHERE id IN<foreach collection="list" item="id" open="(" close=")" separator=",">#{id}</foreach></delete></mapper>🔍 关键标签说明:
| 标签/属性 | 作用 |
|---|---|
useGeneratedKeys="true" | 启用数据库自动生成主键(如 MySQL 的 AUTO_INCREMENT) |
keyProperty="id" | 将生成的主键回填到实体类的 id 字段 |
<trim> | 动态拼接 SQL,避免多余逗号 |
<set> | 用于 UPDATE,自动处理 SET 子句末尾的逗号 |
<foreach> | 遍历集合,常用于 IN 查询或批量操作 |
四、在非 Web 环境中测试 CRUD
方式一:使用 CommandLineRunner
// 在主启动类中添加
@Bean
public CommandLineRunner crudTest(UserMapper userMapper) {return args -> {System.out.println("=== 1. 新增用户 ===");User user = new User("Tom", "tom@example.com");userMapper.insert(user);System.out.println("插入后 ID: " + user.getId());System.out.println("\n=== 2. 查询所有 ===");userMapper.findAll().forEach(System.out::println);System.out.println("\n=== 3. 修改用户 ===");user.setEmail("tom_new@example.com");userMapper.updateById(user);System.out.println("更新后的用户: " + userMapper.findById(user.getId()));System.out.println("\n=== 4. 选择性新增(仅 name)===");User partialUser = new User();partialUser.setName("PartialUser");// email 为 null,不会插入userMapper.insertSelective(partialUser);System.out.println("部分插入结果: " + partialUser);System.out.println("\n=== 5. 批量删除 ===");List<Integer> idsToDelete = Arrays.asList(partialUser.getId(), user.getId());int deleted = userMapper.deleteByIds(idsToDelete);System.out.println("删除记录数: " + deleted);};
}记得导入:
import java.util.Arrays;方式二:JUnit 单元测试(推荐用于验证逻辑)
// src/test/java/com/example/mybatisdemo/UserCrudTest.java
@SpringBootTest
public class UserCrudTest {@Autowiredprivate UserMapper userMapper;@Testvoid testInsertAndUpdate() {// 插入User u = new User("JUnit", "junit@test.com");userMapper.insert(u);assertThat(u.getId()).isNotNull();// 修改u.setName("Updated JUnit");userMapper.updateById(u);User updated = userMapper.findById(u.getId());assertThat(updated.getName()).isEqualTo("Updated JUnit");// 删除userMapper.deleteById(u.getId());assertThat(userMapper.findById(u.getId())).isNull();}@Testvoid testInsertSelective() {User u = new User();u.setName("OnlyName");// email 为 nulluserMapper.insertSelective(u);assertThat(u.getId()).isNotNull();User saved = userMapper.findById(u.getId());assertThat(saved.getEmail()).isNull(); // 确保 email 未写入userMapper.deleteById(u.getId());}
}五、注意事项与最佳实践
✅ 主键回填
- 必须设置
useGeneratedKeys="true"和keyProperty - 适用于自增主键(MySQL、SQL Server),Oracle 需用
<selectKey>
✅ 选择性操作(避免覆盖 null)
- 使用
<if>判断字段是否为 null - 防止
update把已有数据设为 null
✅ 批量操作性能
- 批量插入可用
<foreach>+INSERT INTO ... VALUES (...), (...), ... - 但注意 SQL 长度限制(MySQL
max_allowed_packet)
✅ 事务控制(重要!)
若需保证多个操作原子性,加上 @Transactional:
@Service
public class UserService {@Autowiredprivate UserMapper userMapper;@Transactionalpublic void batchOperation() {userMapper.insert(new User("A", "a@test.com"));userMapper.insert(new User("B", "b@test.com"));// 若中间出错,全部回滚}
}并在主类启用事务:
@SpringBootApplication
@EnableTransactionManagement // 启用事务(Spring Boot 通常自动开启,但显式声明更安全)
public class MybatisDemoApplication { ... }六、总结
| 操作 | XML 标签 | 关键技巧 |
|---|---|---|
| 新增 | <insert> | useGeneratedKeys + keyProperty |
| 修改 | <update> | <set> + <if> 实现选择性更新 |
| 删除 | <delete> | <foreach> 支持批量 |
| 安全 | — | 使用选择性操作避免 null 覆盖 |
| 可靠 | — | 结合 @Transactional 保证数据一致性 |
