乐途乐途
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
  • 学习路径
  • 第1章 MyBatis概述与快速上手

    • 本章定位
    • MyBatis简介
    • 环境搭建
    • 第一个MyBatis程序
    • SqlSessionFactoryBuilder与openSession重载
    • SqlSessionFactory与SqlSession
    • SqlSession核心方法
    • 不使用 XML 构建 SqlSessionFactory
    • Mapper接口与映射方式
    • Java API 目录结构
  • 第2章 全局配置文件详解

    • 本章定位
    • properties
    • settings
    • typeAliases
    • typeHandlers
    • objectFactory
    • plugins
    • environments
    • transactionManager
    • dataSource
    • databaseIdProvider
    • mappers
    • 日志配置
  • 第3章 SQL映射文件基础

    • 本章定位
    • select
    • insert
    • update
    • delete
    • 参数传递与占位符
    • 主键生成策略
    • resultType
    • resultMap
    • 自动映射详解
    • sql片段
    • SQL 语句构建器
  • 第4章 动态SQL

    • 本章定位
    • if
    • choose、when、otherwise
    • where
    • set
    • foreach
    • trim
    • bind
    • script 元素:在注解映射器中启用动态 SQL
    • _databaseId 与动态 SQL 的多数据库支持
    • 动态 SQL 中插入脚本语言
  • 第5章 结果映射与关联查询

    • 本章定位
    • resultMap详解
    • association
    • collection
    • discriminator
    • N+1查询问题
    • 延迟加载
  • 第6章 MyBatis注解开发

    • 本章定位
    • @Select
    • @Insert
    • @Update
    • @Delete
    • @Param
    • @Options
    • @SelectKey
    • @Results
    • @Result
    • @One
    • @Many
    • @SelectProvider
  • 第7章 缓存与性能优化

    • 本章定位
    • 一级缓存
    • 二级缓存
    • 缓存配置详解
    • 自定义缓存
    • Executor执行器类型
    • 分页插件

@Insert

导学

本节学习目标:

  • 掌握 @Insert 注解的基本用法,能够替代 XML 中的 <insert> 标签完成数据插入
  • 学会配合 @Options 实现自增主键的自动回填
  • 理解批量插入在注解模式下的实现方式(<script> 标签配合 <foreach>)
  • 明确注解插入与 XML 插入在事务控制和主键获取上的行为一致性

定义

@Insert 是 MyBatis 提供的核心插入注解,标注在 Mapper 接口的方法上,用于声明插入语句。它对应 XML 映射文件中的 <insert> 元素。

痛点解决:在纯注解开发中,开发者无需编写 XML 即可声明 INSERT SQL,同时通过 @Options 或 @SelectKey 解决主键回填这一插入场景的核心诉求,使接口文件成为插入操作的唯一维护点。

注解方式 vs XML 方式对比

对比维度@Insert 注解方式XML <insert> 方式
主键回填需额外加 @Options 或 @SelectKey直接在 <insert> 标签内写 useGeneratedKeys
批量插入需用 <script> 标签写 <foreach>原生支持 <foreach>,更直观
代码集中度SQL、主键策略、方法签名在一处SQL 与接口分离,策略分散在 XML 属性中
维护成本单文件维护,适合简单插入适合需要频繁调整字段映射的复杂插入

适用场景建议:单条插入且需要主键回填时,@Insert + @Options 非常简洁;批量插入或插入逻辑极复杂时,建议评估是否使用 XML 或 @InsertProvider。

适用位置与核心属性

@Insert 只能标注在 Mapper 接口的方法 上。

属性类型必填说明
valueString是要执行的 INSERT SQL 语句,支持 #{} 引用传入对象的属性

核心原理

MyBatis 解析到 @Insert 后,将其注册为 MappedStatement,语句类型标记为 INSERT。执行时,MyBatis 先通过反射提取参数对象的属性值,绑定到 SQL 的 #{} 占位符;JDBC 执行插入后,如果配置了 @Options(useGeneratedKeys = true),MyBatis 会调用 Statement.getGeneratedKeys() 获取数据库生成的主键,并反射回填到参数对象的指定属性中。

完整示例

场景说明

乐途公司学生管理系统需要实现:新增一名学生并自动获取自增主键;同时支持一次性批量导入多名学生。

操作前的数据库表结构及初始数据

CREATE TABLE student (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(20),
    age INT,
    major VARCHAR(20),
    score DECIMAL(5,2)
);

INSERT INTO student (name, age, major, score) VALUES
('大翔', 22, '计算机科学', 95.5),
('白歌', 21, '软件工程', 88.0),
('小崔', 20, '计算机科学', 92.0),
('黄俪', 21, '信息安全', 90.5),
('李眉', 22, '软件工程', 87.0);

当前数据状态:

idnameagemajorscore
1大翔22计算机科学95.50
2白歌21软件工程88.00
3小崔20计算机科学92.00
4黄俪21信息安全90.50
5李眉22软件工程87.00

完整的注解接口代码

实体类

package com.flying.entity;

public class Student {
    private Integer id;
    private String name;
    private Integer age;
    private String major;
    private Double score;

    // Getter 和 Setter
    public 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 Integer getAge() { return age; }
    public void setAge(Integer age) { this.age = age; }
    public String getMajor() { return major; }
    public void setMajor(String major) { this.major = major; }
    public Double getScore() { return score; }
    public void setScore(Double score) { this.score = score; }
}

Mapper 接口

package com.flying.mapper;

import com.flying.entity.Student;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Param;
import java.util.List;

public interface StudentMapper {

    /**
     * 插入单条记录,并获取自增主键回填到对象中
     */
    @Insert("INSERT INTO student(name, age, major, score) VALUES(#{name}, #{age}, #{major}, #{score})")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    int insertStudent(Student student);

    /**
     * 批量插入学生
     * 使用 <script> 标签包裹 XML 动态 SQL 的 <foreach>
     */
    @Insert("<script>" +
            "INSERT INTO student(name, age, major, score) VALUES " +
            "<foreach collection='list' item='item' separator=','>" +
            "(#{item.name}, #{item.age}, #{item.major}, #{item.score})" +
            "</foreach>" +
            "</script>")
    int batchInsert(@Param("list") List<Student> students);
}

测试调用代码

package com.flying.test;

import com.flying.entity.Student;
import com.flying.mapper.StudentMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

public class InsertTest {
    public static void main(String[] args) throws Exception {
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
        SqlSession session = factory.openSession();

        StudentMapper mapper = session.getMapper(StudentMapper.class);

        // 1. 单条插入并获取主键
        System.out.println("=== 单条插入 ===");
        Student newStu = new Student();
        newStu.setName("赵新");
        newStu.setAge(23);
        newStu.setMajor("人工智能");
        newStu.setScore(91.0);

        int rows = mapper.insertStudent(newStu);
        System.out.println("影响行数: " + rows);
        System.out.println("回填主键 id: " + newStu.getId());

        // 2. 批量插入
        System.out.println("\n=== 批量插入 ===");
        List<Student> batch = new ArrayList<>();
        Student s1 = new Student(); s1.setName("钱多多"); s1.setAge(20); s1.setMajor("网络工程"); s1.setScore(85.0);
        Student s2 = new Student(); s2.setName("孙小空"); s2.setAge(21); s2.setMajor("网络工程"); s2.setScore(89.5);
        batch.add(s1);
        batch.add(s2);

        int batchRows = mapper.batchInsert(batch);
        System.out.println("影响行数: " + batchRows);

        session.commit();
        session.close();
    }
}

实际执行结果

控制台 SQL 输出

=== 单条插入 ===
[main] DEBUG com.flying.mapper.StudentMapper.insertStudent - ==>  Preparing: INSERT INTO student(name, age, major, score) VALUES(?, ?, ?, ?)
[main] DEBUG com.flying.mapper.StudentMapper.insertStudent - ==> Parameters: 赵新(String), 23(Integer), 人工智能(String), 91.0(Double)
[main] DEBUG com.flying.mapper.StudentMapper.insertStudent - <==    Updates: 1
影响行数: 1
回填主键 id: 6

=== 批量插入 ===
[main] DEBUG com.flying.mapper.StudentMapper.batchInsert - ==>  Preparing: INSERT INTO student(name, age, major, score) VALUES (?, ?, ?, ?) , (?, ?, ?, ?)
[main] DEBUG com.flying.mapper.StudentMapper.batchInsert - ==> Parameters: 钱多多(String), 20(Integer), 网络工程(String), 85.0(Double), 孙小空(String), 21(Integer), 网络工程(String), 89.5(Double)
[main] DEBUG com.flying.mapper.StudentMapper.batchInsert - <==    Updates: 2
影响行数: 2

插入后数据状态

idnameagemajorscore
1大翔22计算机科学95.50
2白歌21软件工程88.00
3小崔20计算机科学92.00
4黄俪21信息安全90.50
5李眉22软件工程87.00
6赵新23人工智能91.00
7钱多多20网络工程85.00
8孙小空21网络工程89.50

分析

  • @Options(useGeneratedKeys = true, keyProperty = "id") 是 MySQL 自增主键回填的标准配置,执行后 student.getId() 从 null 变为 6
  • 批量插入借助 <script> 标签在注解中嵌入 XML 动态 SQL,<foreach> 遍历集合生成多组 VALUES,这是注解模式下实现批量的主流做法
  • 批量插入时务必使用 @Param("list") 明确参数名,否则 MyBatis 默认以 list 或 collection 作为键,在部分 JDK 版本下可能因参数名丢失而找不到集合

易错场景 / 常见误区

误区错误示例正解
忘记加 @Options 导致主键未回填只写 @Insert增加 @Options(useGeneratedKeys = true, keyProperty = "id")
keyProperty 写错属性名keyProperty = "studentId"必须与实体类属性名一致,如 id
批量插入时参数未加 @Paramint batchInsert(List<Student> list)写成 int batchInsert(@Param("list") List<Student> list)
批量插入后忘记提交事务无 session.commit()插入属于写操作,必须手动 commit 或开启自动提交

面试考点

Q1:@Insert 如何获取数据库自增主键?

配合 @Options(useGeneratedKeys = true, keyProperty = "id") 使用。MyBatis 在执行 INSERT 后调用 JDBC 的 getGeneratedKeys() 获取主键,并通过反射回填到传入实体对象的 id 属性中。

Q2:注解方式下批量插入有哪些实现方式?

常见三种:(1) 在 @Insert 中使用 <script> 标签包裹 <foreach> 生成多条 VALUES;(2) 使用 @InsertProvider 在 Java 代码中拼接批量 SQL;(3) 借助 MySQL 的 rewriteBatchedStatements=true 配合循环单条插入提升性能。

Q3:useGeneratedKeys 对非自增主键的数据库有效吗?

无效。useGeneratedKeys 依赖数据库的自动生成键能力(如 MySQL 自增、SQL Server Identity)。对于 Oracle 序列等场景,应使用 @SelectKey 在插入前或插入后查询主键。

Q4:为什么批量插入时建议加 @Param?

JDK 8 之前编译默认不带参数名信息,MyBatis 无法通过反射获取形参名。加 @Param("list") 后,MyBatis 明确知道集合在参数 Map 中的 key 是 list,<foreach collection='list'> 才能正确解析。

小结

@Insert 让插入操作在注解模式下变得简洁直观,配合 @Options 可一站式解决主键回填问题。对于批量场景,通过 <script> 标签引入 <foreach> 是注解开发中的常用技巧。需要更复杂的插入逻辑(如多表插入、动态字段)时,可继续学习 @InsertProvider 或评估 XML 方案。

下一章引子

插入之后往往是数据修正。@Update 注解负责替代 XML 的 <update> 完成数据更新,并且可以直接返回影响行数用于业务校验。下一节将详细讲解 @Update 的用法。

上一页
@Select
下一页
@Update