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

    • 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

导学

本节将掌握MyBatis中insert映射元素的配置与使用。你将学会如何编写插入语句,理解useGeneratedKeys与keyProperty的协作机制,实现插入数据后自动获取MySQL自增主键并回填到实体对象中,同时掌握insert的核心属性与执行原理。

定义

insert是SQL映射文件中用于定义插入语句的元素。在JDBC原始写法中,插入一条记录需要手动编写INSERT INTO语句、设置参数、执行更新,若需要获取数据库生成的主键,还要额外调用Statement.getGeneratedKeys()并遍历ResultSet提取主键值。insert元素将这一切声明化:写好SQL后,MyBatis自动完成参数绑定、执行插入,并可通过配置一键实现主键回填,彻底告别主键获取的样板代码。

适用位置与核心属性

insert元素书写在映射文件的<mapper>根标签内部,与select、update、delete并列。

属性是否必填说明
id是映射语句的唯一标识,与Mapper接口方法名对应
parameterType否传入参数的全限定类名或别名,MyBatis可自动推断
useGeneratedKeys否是否使用JDBC的getGeneratedKeys获取数据库生成的主键,默认false
keyProperty否将获取到的主键值封装到实体对象的哪个属性中
keyColumn否当主键列名与keyProperty不一致时,指定数据库的主键列名
flushCache否执行后是否清空本地缓存与二级缓存,默认true
timeout否驱动等待数据库返回的最大秒数,默认未设置

useGeneratedKeys与keyProperty必须配合使用,缺一不可。keyColumn仅在主键列名与实体属性名不一致时才需要显式指定。

核心原理

MyBatis执行insert语句时,不仅完成数据插入,还能在数据库生成主键后将其自动回填到Java对象中。

  1. Java调用:开发者调用StudentMapper.insert(student),此时student.getId()通常为null。
  2. SqlSession & Executor:会话层调度执行器,准备执行插入。
  3. StatementHandler:创建PreparedStatement,绑定#{name}、#{age}等参数。
  4. JDBC执行:向MySQL 5.7发送INSERT语句,数据库完成数据写入并生成自增主键。
  5. 获取生成主键:若配置了useGeneratedKeys=true,MyBatis调用Statement.getGeneratedKeys()获取主键结果集。
  6. 反射回填:通过MetaObject工具将主键值反射设置到student对象的id属性中。
  7. 返回:插入完成后,Java对象的主键属性已被赋值,开发者可直接通过student.getId()获取新主键。

完整示例

场景说明

乐途公司技术部新入职了两名实习生,需要将他们录入学生管理系统。录入后系统需要立即获取数据库分配的自增ID,以便后续为其分配工位和权限。本节演示插入单条记录并获取自增主键的完整流程。

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

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

完整的映射文件片段与Java代码

POJO类

package com.flywing.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.flywing.mapper;

import com.flywing.entity.Student;

public interface StudentMapper {
    // 插入学员并获取自增主键
    int insertStudent(Student student);
}

映射文件 StudentMapper.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.flywing.mapper.StudentMapper">

    <!--
        useGeneratedKeys="true" 启用JDBC获取生成主键
        keyProperty="id"        将获取到的主键值回填到Student对象的id属性
    -->
    <insert id="insertStudent"
            parameterType="com.flywing.entity.Student"
            useGeneratedKeys="true"
            keyProperty="id">
        INSERT INTO student (name, age, major, score)
        VALUES (#{name}, #{age}, #{major}, #{score})
    </insert>

</mapper>

测试代码

package com.flywing.test;

import com.flywing.entity.Student;
import com.flywing.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;

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);

        // 准备新学员数据
        Student newbie = new Student();
        newbie.setName("乐途实习生A");
        newbie.setAge(19);
        newbie.setMajor("人工智能");
        newbie.setScore(85.0);

        System.out.println("插入前对象ID:" + newbie.getId()); // null

        // 执行插入
        int rows = mapper.insertStudent(newbie);
        session.commit(); // 必须提交事务

        System.out.println("影响行数:" + rows);
        System.out.println("插入后对象ID:" + newbie.getId()); // 数据库生成的自增ID

        session.close();
    }
}

实际执行结果

控制台SQL输出

[DEBUG] com.flywing.mapper.StudentMapper.insertStudent - ==>  Preparing: INSERT INTO student (name, age, major, score) VALUES (?, ?, ?, ?)
[DEBUG] com.flywing.mapper.StudentMapper.insertStudent - ==> Parameters: 乐途实习生A(String), 19(Integer), 人工智能(String), 85.0(Double)
[DEBUG] com.flywing.mapper.StudentMapper.insertStudent - <==    Updates: 1
插入前对象ID:null
影响行数:1
插入后对象ID:6

插入后的数据库状态

idnameagemajorscore
1大翔22计算机科学95.50
2白歌21软件工程88.00
3小崔20计算机科学92.00
4黄俪21信息安全90.50
5李眉22软件工程87.00
6乐途实习生A19人工智能85.00

分析

  1. 主键回填机制:useGeneratedKeys="true"告诉MyBatis在插入后调用JDBC的getGeneratedKeys()方法。MyBatis 3.5.x内部通过Jdbc3KeyGenerator处理这一逻辑,获取到主键后利用MetaObject反射将值注入keyProperty指定的属性。
  2. 返回值含义:insert方法的返回值int表示受影响的行数,不是主键值。主键值通过传入的实体对象本身带回。
  3. 事务提交:MyBatis默认不自动提交事务,插入后必须调用session.commit(),否则数据不会真正写入数据库,主键也不会生成。

易错场景/常见误区

误区正解
认为insert的返回值就是生成的主键返回值是受影响的行数;主键通过keyProperty回填到传入的实体对象中
只写useGeneratedKeys不写keyProperty二者必须同时出现,否则MyBatis不知道将主键值放到哪里
插入后忘记session.commit()MyBatis默认手动提交,不提交则事务回滚,数据不会持久化
在keyProperty中写数据库列名idkeyProperty写的是Java实体属性名,不是数据库列名;若不一致用keyColumn指定列名
批量插入时也用useGeneratedKeys批量插入时部分驱动对getGeneratedKeys支持不完善,可能只能回填第一个对象的主键;批量主键获取建议使用selectKey(见"主键生成策略"一节)

面试考点

Q1:MyBatis插入后如何获取数据库生成的主键?

在insert元素中配置useGeneratedKeys="true"和keyProperty="id"。MyBatis会在插入完成后调用JDBC的Statement.getGeneratedKeys()获取主键,并通过反射将主键值回填到传入实体对象的id属性中。注意返回值int只是影响行数,主键要从对象中获取。

Q2:keyProperty和keyColumn有什么区别?

keyProperty指定Java实体对象中接收主键值的属性名;keyColumn指定数据库表中的主键列名。当实体属性名与数据库列名不一致时,需要同时配置二者,例如keyProperty="stuId" keyColumn="id"。

Q3:为什么插入后必须手动commit?

MyBatis的SqlSession默认开启手动提交模式(openSession()等价于openSession(false))。这是为了将多个操作打包在一个事务中,保证数据一致性。若需要自动提交,可调用openSession(true),但生产环境不推荐。

Q4:如果数据库不支持自增主键(如Oracle),如何获取插入后的主键?

应使用selectKey元素,在插入前或插入后执行查询获取主键值(如查询Oracle序列的NEXTVAL),再赋值给实体对象。详见"主键生成策略"一节。

小结

insert元素将数据插入与主键获取流程完全声明化。通过useGeneratedKeys与keyProperty的配合,MyBatis能够在插入完成后自动将MySQL自增主键回填到Java实体中,避免了JDBC时代手动处理getGeneratedKeys()的繁琐代码。记住三个关键点:配置useGeneratedKeys和keyProperty、通过实体对象获取主键、插入后务必提交事务。

下一章引子

数据插入后,难免需要修改。学员转专业、分数更正、年龄更新都是常见场景。update元素负责处理数据修改,它的配置比insert更简洁,但同样需要注意返回值和事务提交。下一节将讲解update的基本用法、影响行数的获取,以及为动态更新做铺垫的基础知识。

上一页
select
下一页
update