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

    • 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执行器类型
    • 分页插件

select

导学

本节将掌握MyBatis中最常用的查询映射元素。你将学会如何配置select语句,理解parameterType与resultType的协作方式,区分selectOne与selectList的适用场景,并能够编写单条件查询、多条件查询以及返回Map、List和单个对象的完整示例。

定义

select是SQL映射文件中用于定义查询语句的元素。在JDBC原始写法中,开发者需要手动拼接SQL、设置参数、执行查询、遍历ResultSet并逐列提取数据封装成对象。select元素将这一整套流程声明化:只需写好SQL,MyBatis自动完成参数绑定、结果集遍历与对象映射,彻底消除样板代码。

适用位置与核心属性

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

属性是否必填说明
id是映射语句的唯一标识,与Mapper接口方法名对应
parameterType否传入参数的全限定类名或别名,MyBatis可自动推断
resultType否*期望返回结果映射到的类名或别名(与resultMap二选一)
resultMap否*引用外部resultMap定义的ID(与resultType二选一)
flushCache否执行后是否清空本地缓存与二级缓存,默认false
useCache否执行结果是否放入二级缓存,默认true
timeout否驱动等待数据库返回的最大秒数,默认未设置
fetchSize否驱动每次从数据库获取的行数,用于优化批量查询
statementType否使用STATEMENT、PREPARED或CALLABLE,默认PREPARED
resultOrdered否是否按嵌套结果顺序返回,用于嵌套结果映射,默认false

*resultType与resultMap必须且只能配置一个。

核心原理

MyBatis执行select语句时,会沿着一条清晰的管道完成从Java方法调用到结果对象返回的全过程。

  1. Java Mapper接口方法:开发者调用StudentMapper.findById(1)。
  2. SqlSession:从SqlSessionFactory获取会话,作为MyBatis对外暴露的核心操作门面。
  3. Executor:根据配置选择简单执行器、复用执行器或缓存执行器,负责调度SQL执行。
  4. StatementHandler:封装JDBC的Statement操作,包括参数设置和结果集处理。
  5. JDBC PreparedStatement:真正与MySQL 5.7通信的预编译语句对象。
  6. ResultSetHandler:将数据库返回的二维结果集,通过反射映射为Java对象的一维列表。
  7. 返回:根据Mapper接口的返回值类型,决定返回List<T>、T或Map<K, V>。

完整示例

场景说明

乐途公司技术部组织了一次MyBatis技术培训,学员为大翔、白歌、小崔、黄俪、李眉。培训结束后,需要查询学生管理系统中的学员信息。本节演示四种典型查询场景:按ID查询单个学员、按专业查询学员列表、多条件组合查询、以及将结果直接封装为Map。

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

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省略
}

Mapper接口

package com.flywing.mapper;

import com.flywing.entity.Student;
import java.util.List;
import java.util.Map;

public interface StudentMapper {
    // 按ID查询单个对象
    Student findById(Integer id);

    // 按专业查询列表
    List<Student> findByMajor(String major);

    // 多条件查询
    List<Student> findByCondition(Map<String, Object> params);

    // 返回Map(key为id,value为对象)
    @MapKey("id")
    Map<Integer, Student> findAllAsMap();
}

映射文件 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">

    <!-- 场景一:按ID查询单个对象 -->
    <select id="findById" parameterType="java.lang.Integer" resultType="com.flywing.entity.Student">
        SELECT id, name, age, major, score
        FROM student
        WHERE id = #{id}
    </select>

    <!-- 场景二:按专业查询列表 -->
    <select id="findByMajor" parameterType="java.lang.String" resultType="com.flywing.entity.Student">
        SELECT id, name, age, major, score
        FROM student
        WHERE major = #{major}
    </select>

    <!-- 场景三:多条件查询 -->
    <select id="findByCondition" parameterType="java.util.HashMap" resultType="com.flywing.entity.Student">
        SELECT id, name, age, major, score
        FROM student
        WHERE major = #{major}
          AND age >= #{minAge}
    </select>

    <!-- 场景四:返回Map -->
    <select id="findAllAsMap" resultType="com.flywing.entity.Student">
        SELECT id, name, age, major, score
        FROM student
    </select>

</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;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class SelectTest {
    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);

        // 场景一:按ID查询
        Student s1 = mapper.findById(1);
        System.out.println("按ID查询:" + s1.getName() + ",专业:" + s1.getMajor());

        // 场景二:按专业查询列表
        List<Student> list = mapper.findByMajor("计算机科学");
        System.out.println("计算机科学专业共 " + list.size() + " 人");
        for (Student s : list) {
            System.out.println("  - " + s.getName() + ",分数:" + s.getScore());
        }

        // 场景三:多条件查询
        Map<String, Object> params = new HashMap<>();
        params.put("major", "软件工程");
        params.put("minAge", 21);
        List<Student> list2 = mapper.findByCondition(params);
        System.out.println("软件工程且年龄≥21的学员:");
        for (Student s : list2) {
            System.out.println("  - " + s.getName() + ",年龄:" + s.getAge());
        }

        // 场景四:返回Map
        Map<Integer, Student> map = mapper.findAllAsMap();
        System.out.println("Map方式获取id=3的学员:" + map.get(3).getName());

        session.close();
    }
}

实际执行结果

控制台SQL输出

[DEBUG] com.flywing.mapper.StudentMapper.findById - ==>  Preparing: SELECT id, name, age, major, score FROM student WHERE id = ?
[DEBUG] com.flywing.mapper.StudentMapper.findById - ==> Parameters: 1(Integer)
[DEBUG] com.flywing.mapper.StudentMapper.findById - <==      Total: 1
按ID查询:大翔,专业:计算机科学

[DEBUG] com.flywing.mapper.StudentMapper.findByMajor - ==>  Preparing: SELECT id, name, age, major, score FROM student WHERE major = ?
[DEBUG] com.flywing.mapper.StudentMapper.findByMajor - ==> Parameters: 计算机科学(String)
[DEBUG] com.flywing.mapper.StudentMapper.findByMajor - <==      Total: 2
计算机科学专业共 2 人
  - 大翔,分数:95.5
  - 小崔,分数:92.0

[DEBUG] com.flywing.mapper.StudentMapper.findByCondition - ==>  Preparing: SELECT id, name, age, major, score FROM student WHERE major = ? AND age >= ?
[DEBUG] com.flywing.mapper.StudentMapper.findByCondition - ==> Parameters: 软件工程(String), 21(Integer)
[DEBUG] com.flywing.mapper.StudentMapper.findByCondition - <==      Total: 2
软件工程且年龄≥21的学员:
  - 白歌,年龄:21
  - 李眉,年龄:22

[DEBUG] com.flywing.mapper.StudentMapper.findAllAsMap - ==>  Preparing: SELECT id, name, age, major, score FROM student
[DEBUG] com.flywing.mapper.StudentMapper.findAllAsMap - <==      Total: 5
Map方式获取id=3的学员:小崔

查询结果集表格(以findByMajor为例)

idnameagemajorscore
1大翔22计算机科学95.50
3小崔20计算机科学92.00

分析

  1. selectOne与selectList的自动识别:MyBatis根据Mapper接口方法的返回值类型自动判断。findById返回Student,内部调用selectOne;findByMajor返回List<Student>,内部调用selectList。若selectOne查询到多条记录,MyBatis会抛出TooManyResultsException。
  2. 参数绑定:#{id}、#{major}等占位符由MyBatis自动解析并调用PreparedStatement.setXxx()完成预编译绑定,防止SQL注入。
  3. 返回Map:@MapKey("id")注解指定用哪一列作为Map的键,resultType指定Map的值类型。若省略@MapKey,则返回Map<String, Object>形式的结果。

易错场景/常见误区

误区正解
resultType与resultMap同时写在一个select中二者只能选其一;简单映射用resultType,复杂映射用resultMap
接口返回单个对象,但SQL实际返回多条记录MyBatis会抛出TooManyResultsException;应确保查询条件能唯一确定记录,或改返回List
parameterType写全限定名导致XML冗长可在mybatis-config.xml中配置<typeAlias>,如<typeAlias alias="Student" type="com.flywing.entity.Student"/>
查询条件为null时,期望查出所有记录#{param}绑定null后SQL为WHERE major = null,不会匹配任何行;应使用动态SQL的if标签处理(第04章详述)
认为select的flushCache默认会清空缓存默认false,查询不会清空缓存;只有insert/update/delete默认才会

面试考点

Q1:MyBatis的selectOne和selectList有什么区别?

selectOne期望返回单条记录,内部调用selectList后取第一条;若结果集大于1条则抛出TooManyResultsException。selectList返回包含零条或多条记录的列表。开发者通常不需要直接调用它们,MyBatis根据Mapper接口的返回值类型自动选择。

Q2:resultType和resultMap能否同时使用?

不能。二者互斥。resultType适用于列名与属性名完全一致的简单场景,MyBatis自动映射;resultMap适用于列名与属性名不一致、需要类型转换或复杂嵌套映射的场景,由开发者显式定义映射规则。

Q3:为什么MyBatis查询默认使用PREPARED statement类型?

PREPARED对应JDBC的PreparedStatement,支持预编译和参数绑定,能有效防止SQL注入并提升重复执行效率。STATEMENT对应普通Statement,直接拼接SQL,存在注入风险且每次执行都需要数据库重新编译SQL。

Q4:Mapper接口方法返回Map<String, Object>时,需要注意什么?

需要在接口方法上添加@MapKey注解指定作为键的列名,且select的resultType应指定为值对象的类型。若希望返回Map<String, Object>(即每一行都是一个列名到值的映射),则接口方法返回类型写Map<String, Object>,且resultType="java.util.HashMap"。

小结

select是MyBatis映射文件中使用频率最高的元素。通过id与Mapper接口方法绑定,parameterType接收外部参数,resultType或resultMap决定结果形态。MyBatis内部通过Executor→StatementHandler→ResultSetHandler的管道完成SQL执行与对象映射,开发者只需关注SQL本身。掌握单条件、多条件、返回List、返回Map四种典型写法,即可覆盖日常80%的查询需求。

下一章引子

查询结果如何映射到Java对象?当数据库列名与实体属性名完全一致时,resultType可以自动完成;但一旦遇到下划线命名与驼峰命名不一致的情况,自动映射就会失效。下一节将深入讲解resultType的适用边界与自动映射原理,并演示基本类型、POJO和Map三种返回形式。

上一页
本章定位
下一页
insert