同一条SQL语句,MyBatis 查询结果与 MySQL 执行结果居然不一致!

本文通过实际案例展示了MyBatis查询结果与MySQL执行结果不一致的问题,通过代码实例演示了问题出现的原因,并提供了针对主键和非主键字段映射的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、前言

不知道大家平时在开发过程中有没有遇到这样的问题,同一条SQL语句,MyBatis 查询结果与 MySQL 执行结果居然不一致,具体说应该是MyBatis 查询结果比MySQL 执行结果的数据更少。不要不相信,如果不注意,这样的坑,你还真有可能会踩的。

二、代码演示

下面用代码简单演示一下:

  1. 创建两张用于测试的数据表:
  • teacher表:
CREATE TABLE `teacher`  (
  `id` int(11) NOT NULL,
  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `scid` int(11) NULL DEFAULT NULL,
  `code` int(255) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

测试数据:

INSERT INTO `teacher`(`id`, `name`, `scid`) VALUES (1, 'jack', 1);
INSERT INTO `teacher`(`id`, `name`, `scid`) VALUES (2, 'jack', 1);
INSERT INTO `teacher`(`id`, `name`, `scid`) VALUES (3, 'jack', 2);
INSERT INTO `teacher`(`id`, `name`, `scid`) VALUES (4, 'jack', 2);
  • school表:
CREATE TABLE `school`  (
  `id` int(11) NOT NULL,
  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `code` int(11) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

测试数据:

INSERT INTO `school`(`id`, `name`, `code`) VALUES (1, '兰陵高中', 1001);
INSERT INTO `school`(`id`, `name`, `code`) VALUES (2, '城东高中', 1002);
  1. 创建一个springboot项目,项目结构如下:
    在这里插入图片描述
  2. 导入maven依赖:
		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.4</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.4</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
  1. 编写application.yml配置文件:
server:
  port: 8070

spring:
  datasource:
    druid:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3306/bg-learnsp?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&useSSL=false
      username: root
      password: root
mybatis:
  mapper-locations: classpath:mapper/*.xml

logging:
  level:
    root: info
    com.learn.mapper: debug

  1. 编写数据库相关实体类:
  • Teacher类:
package com.learn.entity;

import lombok.Data;

@Data
public class Teacher {

    private int id;

    private String name;

    private int scid;

	private int code;
}
  • School类:
package com.learn.entity;

import lombok.Data;

import java.util.List;

@Data
public class School {

    private int id;

    private int code;

    private String name;
	
	// School 与Teacher 是一对多的关系
    private List<Teacher> teachers;

}
  1. 编写school相关mapper:
  • SchoolMapper.java:
package com.learn.mapper;

import com.learn.entity.School;

import java.util.List;

public interface SchoolMapper {

    List<School> selectAll();
}
  • SchoolMapper.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.learn.mapper.SchoolMapper">
    <resultMap id="BaseResultMap" type="com.learn.entity.School">
        <id column="id" jdbcType="INTEGER" property="id"/>
        <result column="code" jdbcType="INTEGER" property="code"/>
        <result column="name" jdbcType="VARCHAR" property="name"/>
        <collection property="teachers" ofType="com.learn.entity.Teacher">
            <result column="tname" jdbcType="VARCHAR" property="name"/>
        </collection>
    </resultMap>

    <select id="selectAll" resultMap="BaseResultMap">
        SELECT
            s.id,
            s.NAME,
            t.NAME AS tname
        FROM
            school s,
            teacher t
        WHERE
            s.id = t.scid
    </select>

</mapper>

  1. SchoolService:
@Service
public class SchoolService {

    @Autowired
    private SchoolMapper schoolMapper;

    public List<School> listAll() {
        return schoolMapper.selectAll();
    }
}
  1. SchoolController:
@RestController
public class SchoolController {

    @Autowired
    private SchoolService schoolService;

    @GetMapping("/listAllSchool")
    public List<School> listAll() {
        return schoolService.listAll();
    }
}
  1. 启动类添加相关配置:
@SpringBootApplication
@MapperScan("com.learn.mapper")
@EnableTransactionManagement
public class SpringbootDataerrorApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootDataerrorApplication.class, args);
    }

}

测试:

上面的school与teacher是一对多的关系,在SchoolMapper.xml文件中使用resultMap标签配置它们的对应映射关系。

启动项目,访问http://localhost:8070/listAllSchool

  • 浏览器显示:
    在这里插入图片描述

  • 日志输出:
    在这里插入图片描述

  • 数据库查询结果:
    在这里插入图片描述
    可以看到,每个高中应该都对应2个jack老师,日志输出的Total也是4,但是查询结果显示时候,每个高中只有一个jack老师了,这是为什么呢?

网上搜索了一下答案:mybaties映射时使用主键判断唯一性,如果查询出来的数据集在映射文件配置中没有设置主键或者主键存在相同的情况下,mybaties只映射其中一条。

解决方法:

知道原因以后,改造一下SchoolMapper.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.learn.mapper.SchoolMapper">
    <resultMap id="BaseResultMap" type="com.learn.entity.School">
        <id column="id" jdbcType="INTEGER" property="id"/>
        <result column="code" jdbcType="INTEGER" property="code"/>
        <result column="name" jdbcType="VARCHAR" property="name"/>
        <collection property="teachers" ofType="com.learn.entity.Teacher">
            <!--增加teacher ID映射-->
            <id column="tid" jdbcType="INTEGER" property="id"/>
            <result column="tname" jdbcType="VARCHAR" property="name"/>
        </collection>
    </resultMap>

    <select id="selectAll" resultMap="BaseResultMap">
        SELECT
            s.id,
            s.NAME,
            t.NAME AS tname,
            t.id as tid
        FROM
            school s,
            teacher t
        WHERE
            s.id = t.scid
    </select>

</mapper>

重新启动,访问:
在这里插入图片描述
可以看到,此时查询数据就是正确的了,但是如果我需要对应的表就是没有主键的呢,此时不能使用主键ID来对应呢?

利用teacher表中的code字段测试一下,修改SchoolMapper.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.learn.mapper.SchoolMapper">
    <resultMap id="BaseResultMap" type="com.learn.entity.School">
        <id column="id" jdbcType="INTEGER" property="id"/>
        <result column="code" jdbcType="INTEGER" property="code"/>
        <result column="name" jdbcType="VARCHAR" property="name"/>
        <collection property="teachers" ofType="com.learn.entity.Teacher">
            <result column="tname" jdbcType="VARCHAR" property="name"/>
            <!--使用code字段映射-->
            <result column="tcode" jdbcType="INTEGER" property="code"/>
        </collection>
    </resultMap>

    <select id="selectAll" resultMap="BaseResultMap">
        SELECT
            s.id,
            s.NAME,
            t.NAME AS tname,
            t.code as tcode
        FROM
            school s,
            teacher t
        WHERE
            s.id = t.scid
    </select>

</mapper>
  • code字段数据相同:
    在这里插入图片描述
    程序查询结果:
    在这里插入图片描述
  • code字段数据不同:
    在这里插入图片描述
    程序查询结果:
    在这里插入图片描述

最后:

可以看到,mybatis在进行查询映射时,如果查询出来的字段值全部一样,会只保留一条数据,这种情况就是造成MyBatis 查询结果与 MySQL 执行结果不一致的主要原因,因此,在进行查询映射的时候,最好增加一个不会重复的字段,最好的选择就是主键,这样就能保证MyBatis 查询结果与 MySQL 执行结果是一致的了!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Coder-文小白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值