Skip to content

前言 ​

一对一叫javaType

一对多叫ofType,都是你定义的实体类型

SQL准备 ​

sql
-- 1. 用户基本表 person
create table person (
    id bigint primary key auto_increment comment '主键',
    username varchar(50) not null comment '用户名',
    age tinyint comment '年龄',
    phone varchar(20) comment '手机号',
    create_time datetime default current_timestamp comment '创建时间',
    update_time datetime default current_timestamp on update current_timestamp comment '更新时间'
) engine=InnoDB default charset=utf8mb4 comment='用户基本信息表';

-- 2. 用户身份证详情表 person_identity 一对一
create table person_identity (
    id bigint primary key auto_increment comment '主键',
    person_id bigint not null unique comment '用户id,unique保证一对一',
    id_card_no varchar(18) not null comment '身份证号',
    real_name varchar(20) not null comment '真实姓名',
    address varchar(200) comment '户籍地址',
    expire_date date comment '有效期',
    foreign key (person_id) references person(id)
) engine=InnoDB default charset=utf8mb4 comment='用户身份证信息表';

-- 3. 用户订单表 person_order 一对多(新增 order_name)
create table person_order (
    id bigint primary key auto_increment comment '订单主键',
    person_id bigint not null comment '用户id',
    order_name varchar(100) not null comment '订单名称',
    order_no varchar(32) not null comment '订单编号',
    order_amount decimal(10,2) not null comment '订单金额',
    order_status tinyint default 0 comment '订单状态 0待支付 1已支付 2已取消',
    create_time datetime default current_timestamp comment '下单时间',
    foreign key (person_id) references person(id)
) engine=InnoDB default charset=utf8mb4 comment='用户订单表';


-- 插入用户
insert into person (username, age, phone) values
('zhangsan', 24, '13800138001'),
('lisi', 28, '13800138002');

-- 插入身份证一对一数据
insert into person_identity (person_id, id_card_no, real_name, address, expire_date) values
(1, '420111199905051234', '张三', '湖北省武汉市江夏区光谷大道108号', '2030-05-05'),
(2, '420111199508084321', '李四', '湖北省武汉市洪山区珞喻路66号', '2028-08-08');

-- 张三 person_id=1,4条订单
insert into person_order (person_id, order_name, order_no, order_amount, order_status) values
(1, '机械键盘', 'ORD20261028004', 520.00, 1),
(1, '华为Mate60手机', 'ORD20261028001', 199.00, 1),
(1, '小米平板6 Pro', 'ORD20261028002', 299.50, 0),
(1, '无线蓝牙耳机', 'ORD20261028003', 89.90, 2);

-- 李四 person_id=2,2条订单
insert into person_order (person_id, order_name, order_no, order_amount, order_status) values
(2, '电竞鼠标', 'ORD20261028005', 129.00, 1),
(2, '27寸显示器支架', 'ORD20261028006', 366.80, 0);

数据表准备 ​

用户身份信息表 ​

主要存储用户的身份证详细信息。

java
package com.cghbh.entity;

import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

/**
 * 用户身份证信息实体
 * 【一对一关系】:一个用户对应一条身份证信息
 * 对应表:person_identity
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("person_identity")
public class PersonIdentity {
    /**
     * 主键id
     */
    private Long id;

    /**
     * 用户id,外键关联person表主键id
     * 该字段加了unique唯一约束,保证一对一
     */
    private Long personId;

    /**
     * 身份证号码,18位
     */
    private String idCardNo;

    /**
     * 身份证上真实姓名
     */
    private String realName;

    /**
     * 身份证户籍地址
     */
    private String address;

    /**
     * 身份证有效期截止日期
     */
    private Date expireDate;
}

用户订单表 ​

存储用户的订单,用户和订单表之间是一对多的关系。

java
package com.cghbh.entity;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.math.BigDecimal;
import java.time.LocalDateTime;

/**
 * 用户订单实体
 * 【一对多关系】:一个用户可以拥有多条订单
 * 对应表:person_order
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("person_order")
public class PersonOrder {
    /**
     * 订单主键id
     */
    private Long id;

    /**
     * 用户id,外键关联person表主键id
     */
    private Long personId;

    /**
     * 订单编号,业务唯一编号
     */
    private String orderNo;

    /**
     * 订单名称,描述本次订单商品/服务名称
     */
    private String orderName;

    /**
     * 订单金额,decimal保留两位小数
     */
    private BigDecimal orderAmount;

    /**
     * 订单状态
     * 0:待支付
     * 1:已支付
     * 2:已取消
     */
    private Integer orderStatus;

    /**
     * 订单创建时间(下单时间)
     */
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
}

用户表 ​

存储用户的基本信息。

java
package com.cghbh.entity;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;
import java.util.List;

/**
 * 用户基础信息实体【主表】
 * 对应表:person
 * 关系:
 * 一对一:和person_identity(身份证信息)
 * 一对多:和person_order(订单列表)
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("person")
public class Person {
    /**
     * 用户主键id
     */
    private Long id;

    /**
     * 用户名(登录昵称)
     */
    private String username;

    /**
     * 用户年龄
     */
    private Integer age;

    /**
     * 用户联系手机号
     */
    private String phone;

    /**
     * 记录创建时间
     */
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;

    /**
     * 记录更新时间,自动更新
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;

    /**
     * 一对一关联:用户身份证信息
     * mybatis resultMap 使用 association 标签映射
     * 不关联到数据库的实体表中
     */
    @TableField(exist = false)
    private PersonIdentity identity;

    /**
     * 一对多关联:用户订单集合,一个用户多个订单
     * mybatis resultMap 使用 collection 标签映射
     * 不关联到数据库的实体表中
     */
    @TableField(exist = false)
    private List<PersonOrder> orderList;
}

基础代码准备 ​

PersonMapper ​

java
package com.cghbh.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cghbh.entity.Person;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface PersonMapper extends BaseMapper<Person> {
    Person getPersonDetailById(Integer id);
}

PersonMapper.xml ​

xml
<?xml version="1.0" encoding="UTF-8" ?>
<!-- 引入mybatis的xml方式编写sql的约束 -->
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cghbh.mapper.PersonMapper">
    <select id="getPersonDetailById" resultType="com.cghbh.entity.Person">
        select p.id,
               p.username,
               p.age,
               p.phone,
               p.create_time,
               p.update_time,
               pi.id          pi_id,
               pi.person_id   pi_person_id,
               pi.id_card_no,
               pi.real_name,
               pi.address,
               pi.expire_date,
               po.id          po_id,
               po.person_id   po_person_id,
               po.order_name,
               po.order_no,
               po.order_amount,
               po.order_status,
               po.create_time po_create_time
        from person p
                 left join person_identity pi on p.id = pi.person_id
                 left join person_order po on p.id = po.person_id
        where p.id = #{id};
    </select>
</mapper>

PersonService ​

java
package com.cghbh.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.cghbh.entity.Person;

public interface PersonService extends IService<Person> {
    /**
     * 根据用户的id查询用户信息,包括关联的身份证信息和订单列表
     *
     * @param id 用户ID
     * @return 用户的全部信息
     */
    Person getPersonDetailById(Integer id);
}

PersonServiceImpl ​

java
package com.cghbh.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cghbh.entity.Person;
import com.cghbh.mapper.PersonMapper;
import com.cghbh.service.PersonService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Slf4j
@RequiredArgsConstructor
@Service
public class PersonServiceImpl extends ServiceImpl<PersonMapper, Person> implements PersonService {
    private final PersonMapper personMapper;

    /**
     * 根据用户的id查询用户信息,包括关联的身份证信息和订单列表
     *
     * @param id 用户ID
     * @return 用户的全部信息
     */
    @Override
    public Person getPersonDetailById(Integer id) {
        return personMapper.getPersonDetailById(id);
    }
}

PersonCotroller ​

java
package com.cghbh.controller;

import com.cghbh.common.Result;
import com.cghbh.entity.Person;
import com.cghbh.service.PersonService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RequiredArgsConstructor
@RestController
@RequestMapping("/persons")
@Slf4j
public class PersonController {
    private final PersonService personService;

    /**
     * 根据用户的id查询用户信息,包括关联的身份证信息和订单列表
     *
     * @param id 用户ID
     * @return 用户的全部信息
     */
    @GetMapping("/{id}")
    public Result getPersonDetailById(@PathVariable Integer id) {
        Person person = personService.getPersonDetailById(id);
        return Result.success(person);
    }
}

PersonMapper最终代码 ​

使用总结

<id column="pi_id" property="id"/>这列是获取主键列的,根据主键列进行去重处理。

<result column="pi_person_id" property="personId"/>是普通字段列。

column:数据库表里面的【字段名】(SQL 查出来的列名)。

property:Java 实体类里面的【属性名】。

association:一对一映射,javaType 指定单个实体类型。

collection:一对多映射,ofType 指定集合里面元素类型,不要写 List。

MyBatis 的作用:把查询结果里 column 的值,赋值给实体的 property 属性。

SQL中查询到的字段和实体中的字段是一致的话可以加autoMapping="true"。

多表联查一定要**字段别名**,避免 id、create_time 重名覆盖。

xml
<?xml version="1.0" encoding="UTF-8" ?>
<!-- 引入mybatis的xml方式编写sql的约束 -->
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cghbh.mapper.PersonMapper">
    <!-- 如果下面SQL中查询到的字段和实体中的字段是一致的话可以加autoMapping="true" -->
    <resultMap id="PersonDetailInfo" type="com.cghbh.entity.Person" autoMapping="true">
        <!-- 主键ID字段,根据这个字段,将查询到的ID相关字段去重 -->
        <id column="id" property="id"/>
        <!-- 普通字段的映射 -->
        <result column="username" property="username"/>
        <result column="age" property="age"/>
        <result column="phone" property="phone"/>
        <result column="create_time" property="createTime"/>
        <result column="update_time" property="updateTime"/>

        <!-- 这是用户信息->用户身份证信息一对一关系的映射 -->
        <!-- 如果下面SQL中查询到的字段和实体中的字段是一致的话可以加autoMapping="true" -->
        <association property="identity" javaType="com.cghbh.entity.PersonIdentity">
            <!-- id用来锁定用户身份证信息数据 -->
            <id column="pi_id" property="id"/>
            <!-- 普通字段的映射 -->
            <result column="pi_person_id" property="personId"/>
            <result column="real_name" property="realName"/>
            <result column="id_card_no" property="idCardNo"/>
            <result column="address" property="address"/>
            <result column="expire_date" property="expireDate"/>
        </association>

        <collection property="orderList" ofType="com.cghbh.entity.PersonOrder">
            <!-- 如果下面SQL中查询到的字段和实体中的字段是一致的话可以加autoMapping="true" -->
            <!-- 这里依赖当前的订单的ID+上面的用户的id来锁定这个集合的列表数据 -->
            <id column="po_id" property="id"/>
            <!-- 普通字段的映射 -->
            <result column="po_person_id" property="personId"/>
            <result column="order_name" property="orderName"/>
            <result column="order_no" property="orderNo"/>
            <result column="order_amount" property="orderAmount"/>
            <result column="order_status" property="orderStatus"/>
            <result column="create_time" property="createTime"/>
        </collection>
    </resultMap>
    <select id="getPersonDetailById" resultMap="PersonDetailInfo">
        select p.id,
               p.username,
               p.age,
               p.phone,
               p.create_time,
               p.update_time,
               pi.id          pi_id,
               pi.person_id   pi_person_id,
               pi.id_card_no,
               pi.real_name,
               pi.address,
               pi.expire_date,
               po.id          po_id,
               po.person_id   po_person_id,
               po.order_name,
               po.order_no,
               po.order_amount,
               po.order_status,
               po.create_time po_create_time
        from person p
                 left join person_identity pi on p.id = pi.person_id
                 left join person_order po on p.id = po.person_id
        where p.id = #{id};
    </select>
</mapper>

返回的数据 ​

json
{
  "code" : 1,
  "msg" : "操作成功",
  "data" : {
    "id" : 2,
    "username" : "lisi",
    "age" : 28,
    "phone" : "13800138002",
    "createTime" : "2026-10-28T11:23:19",
    "updateTime" : "2026-10-28T11:23:19",
    "identity" : {
      "id" : 2,
      "personId" : 2,
      "idCardNo" : "420111199508084321",
      "realName" : "李四",
      "address" : "湖北省武汉市洪山区珞喻路66号",
      "expireDate" : "2028-08-07T16:00:00.000+00:00"
    },
    "orderList" : [ {
      "id" : 5,
      "personId" : 2,
      "orderNo" : "ORD20261028005",
      "orderName" : "电竞鼠标",
      "orderAmount" : 129.0,
      "orderStatus" : 1,
      "createTime" : "2026-10-28T11:23:19"
    }, {
      "id" : 6,
      "personId" : 2,
      "orderNo" : "ORD20261028006",
      "orderName" : "27寸显示器支架",
      "orderAmount" : 366.8,
      "orderStatus" : 0,
      "createTime" : "2026-10-28T11:23:19"
    } ]
  }
}