前言
一对一叫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"
} ]
}
}