MyBatis 一对一、一对多 SQL

MyBatis 一对一、一对多 SQL + 完整示例(MySQL)

说明:两种写法:关联查询(join,一次 SQL) 、嵌套查询(子查询,多条 SQL),日常开发 join 用的更多 实体、Mapper 接口、xml、SQL 全部给完整可运行例子

表设计

java 复制代码
-- 用户表(主表)
CREATE TABLE t_user (
  id INT PRIMARY KEY AUTO_INCREMENT,
  username VARCHAR(50),
  age INT
);

-- 用户详情表:一对一 【一个用户 对应 一条详情】
CREATE TABLE t_user_info (
  id INT PRIMARY KEY AUTO_INCREMENT,
  user_id INT UNIQUE, -- 唯一,一对一关键
  address VARCHAR(100),
  phone VARCHAR(20)
);

-- 订单表:一对多 【一个用户 对应 多个订单】
CREATE TABLE t_order (
  id INT PRIMARY KEY AUTO_INCREMENT,
  user_id INT,
  order_no VARCHAR(32),
  amount DECIMAL(10,2)
);

一、一对一:User → UserInfo

实体类

java 复制代码
// User.java
@Data
public class User {
    private Integer id;
    private String username;
    private Integer age;
    // 一对一:用户详情
    private UserInfo userInfo;
}

// UserInfo.java
@Data
public class UserInfo {
    private Integer id;
    private Integer userId;
    private String address;
    private String phone;
}

Mapper 接口

java 复制代码
public interface UserMapper {
    // 根据id查询用户(带一对一详情)
    User findUserWithInfoById(Integer id);
}

Mapper.xml 【方式 1:JOIN 关联查询(推荐,单条 SQL)】

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.mapper.UserMapper">

    <!-- 一对一结果映射 -->
    <resultMap id="UserWithInfoMap" type="User">
        <id column="u_id" property="id"/>
        <result column="username" property="username"/>
        <result column="age" property="age"/>
        <!-- 一对一 association -->
        <association property="userInfo" javaType="UserInfo">
            <id column="info_id" property="id"/>
            <result column="user_id" property="userId"/>
            <result column="address" property="address"/>
            <result column="phone" property="phone"/>
        </association>
    </resultMap>

    <select id="findUserWithInfoById" resultMap="UserWithInfoMap">
        SELECT
            u.id u_id,
            u.username,
            u.age,
            ui.id info_id,
            ui.user_id,
            ui.address,
            ui.phone
        FROM t_user u
        LEFT JOIN t_user_info ui ON u.id = ui.user_id
        WHERE u.id = #{id}
    </select>

</mapper>

一对一【方式 2:嵌套子查询(两条 SQL,association + select)】

XML 复制代码
<resultMap id="UserWithInfoSubMap" type="User">
    <id column="id" property="id"/>
    <result column="username" property="username"/>
    <result column="age" property="age"/>
    <!-- 嵌套查询:单独执行findInfoByUserId -->
    <association property="userInfo" javaType="UserInfo" select="findInfoByUserId" column="id"/>
</resultMap>

<select id="findUserWithInfoByIdSub" resultMap="UserWithInfoSubMap">
    SELECT id,username,age FROM t_user WHERE id=#{id}
</select>

<select id="findInfoByUserId" resultType="UserInfo">
    SELECT id,user_id,address,phone FROM t_user_info WHERE user_id = #{id}
</select>

二、一对多:User → List<Order>

实体类

java 复制代码
@Data
public class User {
    private Integer id;
    private String username;
    private Integer age;
    // 一对多:多个订单
    private List<Order> orderList;
}

@Data
public class Order {
    private Integer id;
    private Integer userId;
    private String orderNo;
    private BigDecimal amount;
}

Mapper 接口

java 复制代码
public interface UserMapper {
    User findUserWithOrderById(Integer id);
}

Mapper.xml 【方式 1:JOIN 关联查询(推荐)】

一对多核心标签:collection

XML 复制代码
<mapper namespace="com.mapper.UserMapper">

    <resultMap id="UserWithOrderMap" type="User">
        <id column="u_id" property="id"/>
        <result column="username" property="username"/>
        <result column="age" property="age"/>
        <!-- 一对多 collection,ofType 指定集合里面元素类型 -->
        <collection property="orderList" ofType="Order">
            <id column="o_id" property="id"/>
            <result column="user_id" property="userId"/>
            <result column="order_no" property="orderNo"/>
            <result column="amount" property="amount"/>
        </collection>
    </resultMap>

    <select id="findUserWithOrderById" resultMap="UserWithOrderMap">
        SELECT
            u.id u_id,
            u.username,
            u.age,
            o.id o_id,
            o.user_id,
            o.order_no,
            o.amount
        FROM t_user u
        LEFT JOIN t_order o ON u.id = o.user_id
        WHERE u.id = #{id}
    </select>

</mapper>

⚠️注意:join 一对多会返回多条行数据,MyBatis 依靠主表 id 相同自动合并成一个 User,订单放进 List,不需要自己去重。

一对多【方式 2:嵌套子查询(多条 SQL,N+1 问题)】

XML 复制代码
<resultMap id="UserWithOrderSubMap" type="User">
    <id column="id" property="id"/>
    <result column="username" property="username"/>
    <result column="age" property="age"/>
    <!-- column 把user的id传给子查询 -->
    <collection property="orderList" ofType="Order" select="findOrderByUserId" column="id"/>
</resultMap>

<select id="findUserWithOrderByIdSub" resultMap="UserWithOrderSubMap">
    SELECT id,username,age FROM t_user WHERE id=#{id}
</select>

<select id="findOrderByUserId" resultType="Order">
    SELECT id,user_id,order_no,amount FROM t_order WHERE user_id=#{id}
</select>

❗嵌套子查询会产生 N+1:查 1 次用户 + N 次订单,数据量大慎用,可以配置 fetchType="lazy" 开启懒加载


关键点对比总结

表格

关系 标签 特点
一对一 <association javaType="实体类"> 单个对象
一对多 <collection ofType="实体类"> List 集合

常用坑提醒

  1. 列名重名!join 查询一定要别名(u_id /o_id),否则映射错乱
  2. 一对多 join 不要加 DISTINCT,会把订单数据去重丢失
  3. 嵌套查询懒加载配置(mybatis-config.xml)
XML 复制代码
<settings>
    <setting name="lazyLoadingEnabled" value="true"/>
    <setting name="aggressiveLazyLoading" value="false"/>
</settings>
相关推荐
Elastic 中国社区官方博客1 小时前
Elastic 在《Forrester Wave™:2026 年第四季度智能运维平台》报告中被评为强劲表现者
大数据·运维·数据库·elasticsearch·搜索引擎·全文检索
Apache IoTDB1 小时前
从 IoTDB 到 CED-DB:哈尔滨工业大学探索云边端协同时序数据库
数据库·时序数据库·iotdb
闲云野鹤在人间1 小时前
头秃修炼手册:StorageClass+StatefulSet+ETCD 全套渡劫笔记
linux·数据库·笔记·云原生·kubernetes·etcd
Decay__2 小时前
03_MySQL 存储引擎与存储管理 —— InnoDB 深入、表空间/ibd、binlog/redo/undo 与双写缓冲
运维·数据库·sql·mysql·架构
瀚高PG实验室2 小时前
瀚高数据库提交数据后,是否需要COMMIT
数据库·postgresql·瀚高数据库
bksczm2 小时前
MySQL程序架构
数据库·mysql·架构
论文复现现场2 小时前
2026 低成本 AI Agent 模型怎么选?DeepSeek V4.1 Flash、Qwen3.8 Flash、GLM-5.3 Flash 对比
数据库·人工智能·qwen·ai agent·glm·deepseek
摇滚侠10 小时前
《On Java 中文版 基础卷》阅读笔记 实现隐藏 12
java·数据库·笔记
川石课堂软件测试11 小时前
学习软件测试需掌握的一些知识
数据库·python·sql·功能测试·网络协议·mysql·http