mybatis多表查询时的问题始末

20 篇文章 0 订阅
4 篇文章 0 订阅
本文详细讲解了MyBatis中多表查询、一对一、一对多、多对多关系的处理,包括实体类设计、resultMap配置及分页实现。通过实例演示如何在复杂数据结构中使用association和collection标签,以及如何解决查询异常和优化性能。
摘要由CSDN通过智能技术生成

多表查询

一对一查询

我们通常在查询数据库时一般是一个实体类对应一张表,但是如果查询的字段不在一张表中该怎么办呢?
先看一个案例吧,从头开始捋------>>
由于是对数据库进行操作,先搞一下数据库,

备好以下两张表,当然你也可用自己的其他表,

DROP TABLE IF EXISTS `dept`;

CREATE TABLE `dept` (
`DEPTNO` int(2) NOT NULL,
`DNAME` varchar(14) DEFAULT NULL,
`LOC` varchar(13) DEFAULT NULL,
PRIMARY KEY (`DEPTNO`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `dept` VALUES ('10', 'ACCOUNTING', 'NEW YORK');
INSERT INTO `dept` VALUES ('20', 'RESEARCH', 'DALLAS');
INSERT INTO `dept` VALUES ('30', 'SALES', 'CHICAGO');
INSERT INTO `dept` VALUES ('40', 'OPERATIONS', 'BOSTON');


DROP TABLE IF EXISTS `emp`;

CREATE TABLE `emp` (
`EMPNO` int(4) NOT NULL,
`ENAME` varchar(10),
`JOB` varchar(9),
`MGR` int(4),
`HIREDATE` date,
`SAL` int(7),
`COMM` int(7),
`DEPTNO` int(2),
PRIMARY KEY (`EMPNO`),
KEY `FK_DEPTNO` (`DEPTNO`),
CONSTRAINT `FK_DEPTNO` FOREIGN KEY (`DEPTNO`) REFERENCES `dept` (`DEPTNO`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;




INSERT INTO `emp` VALUES ('7369', 'SMITH', 'CLERK', '7902', '1980-12-17', '800', null, '20');
INSERT INTO `emp` VALUES ('7499', 'ALLEN', 'SALESMAN', '7698', '1981-02-20', '1600', '300', '30');
INSERT INTO `emp` VALUES ('7521', 'WARD', 'SALESMAN', '7698', '1981-02-22', '1250', '500', '30');
INSERT INTO `emp` VALUES ('7566', 'JONES', 'MANAGER', '7839', '1981-04-02', '2975', null, '20');
INSERT INTO `emp` VALUES ('7654', 'MARTIN', 'SALESMAN', '7698', '1981-09-28', '1250', '1400', '30');
INSERT INTO `emp` VALUES ('7698', 'BLAKE', 'MANAGER', '7839', '1981-05-01', '2850', null, '30');
INSERT INTO `emp` VALUES ('7782', 'CLARK', 'MANAGER', '7839', '1981-06-09', '2450', null, '10');
INSERT INTO `emp` VALUES ('7788', 'SCOTT', 'ANALYST', '7566', '1987-04-19', '3000', null, '20');
INSERT INTO `emp` VALUES ('7839', 'KING', 'PRESIDENT', null, '1981-11-17', '5000', null, '10');
INSERT INTO `emp` VALUES ('7844', 'TURNER', 'SALESMAN','7698', '1981-09-08', '1500', '0', '30');
INSERT INTO `emp` VALUES ('7876', 'ADAMS', 'CLERK', '7788', '1987-05-23', '1100', null, '20');
INSERT INTO `emp` VALUES ('7900', 'JAMES', 'CLERK', '7698', '1981-12-03', '950', null, '30');
INSERT INTO `emp` VALUES ('7902', 'FORD', 'ANALYST', '7566', '1981-12-03', '3000', null, '20');
INSERT INTO `emp` VALUES ('7934', 'MILLER', 'CLERK', '7782', '1982-01-23', '1300', null, '10');
 

新建一个maven模块或者新建一个maven工程都可以;
然后准备实体类和对应映射;
这么多字段,就不用手动去编写实体类了,通过mybatis逆向工程自动生成是一个不错的选择;

逆向工程详解链接----->>>

基本架构就出来了
在这里插入图片描述
初步检查一下映射文件,实体类路径是否匹配;

然后开始写mapper映射文件

先分析以一下要查询的数据
在这里插入图片描述
发现并不在一个实体类上,那么怎么办?

新建一个实体类,作用是用于接收查询的数据
新实体类暂且叫EmpDept吧,同时还要配置映射文件

在这里插入图片描述

根据要查询的数据来编写实体类并配置映射文件

你会发现,要原来的实体类和mapper有何用?
如果查询不同的数据,那么每次都要有一个新的实体类???
很显然是不可以的;

那怎吗办呢?

可不可以在一个实体里面引入另一个实体类?
这样就可以减少新建立一个实体类

不妨这么干----从员的角度出发,一个员工对应一个部门
在emp实体类中添加一个dept属性;

在这里插入图片描述
这样我们减少一些不必要的类与映射

一对多查询

如果从部门的角度出发,一个部门对应好几个员工,那么我们的实体类该怎么修改呢?

首先更改dept实体中的属性----增加 emp属性

在这里插入图片描述
然后配置映射文件
在这里插入图片描述

最后测试---->>
在这里插入图片描述

小结

一对一 resultmap用的是association 标签

在 association 元素中通常使用以下属性------>>>
property:指定映射到实体类的对象属性。
column:指定表中对应的字段(即查询返回的列名)。
javaType:指定映射到实体对象属性的类型。

一对多 resultmap用的是 collection 标签

在 collection 元素中通常使用以下属性。
property:指定映射到实体类的对象属性。
column:指定表中对应的字段(即查询返回的列名)。
javaType:指定映射到实体对象属性的类型。
select:指定引入嵌套查询的子 SQL 语句,该属性用于关联映射中的嵌套查询。

多对多查询

首先准备一个数据库然后建表,当然不用自己建,网上有很多可以拿过来练习的虚拟表格,拿来用就可以了’

下面是完整的sql数据,有些顺序需要自己调整,添加外加约束的表要放在后面创建;


SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for userinfo
-- ----------------------------
DROP TABLE IF EXISTS `userinfo`;
CREATE TABLE `userinfo`  (
  `uid` int(0) NOT NULL AUTO_INCREMENT,
  `uname` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `upwd` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`uid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of userinfo
-- ----------------------------
INSERT INTO `userinfo` VALUES (1, '李二狗', '123');
INSERT INTO `userinfo` VALUES (2, '张化胡', '456');
INSERT INTO `userinfo` VALUES (3, '赵小红', '123');
INSERT INTO `userinfo` VALUES (4, '李晓明', '345');
INSERT INTO `userinfo` VALUES (5, '杨小胤', '123');
INSERT INTO `userinfo` VALUES (6, '谷小乐', '789');

SET FOREIGN_KEY_CHECKS = 1;

SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for orderinfo
-- ----------------------------
DROP TABLE IF EXISTS `orderinfo`;
CREATE TABLE `orderinfo`  (
  `oid` int(0) NOT NULL AUTO_INCREMENT,
  `ordernum` int(0) NULL DEFAULT NULL,
  `userId` int(0) NULL DEFAULT NULL,
  PRIMARY KEY (`oid`) USING BTREE,
  INDEX `userId`(`userId`) USING BTREE,
  CONSTRAINT `orderinfo_ibfk_1` FOREIGN KEY (`userId`) REFERENCES `userinfo` (`uid`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of orderinfo
-- ----------------------------
INSERT INTO `orderinfo` VALUES (1, 20200107, 1);
INSERT INTO `orderinfo` VALUES (2, 20200806, 2);
INSERT INTO `orderinfo` VALUES (3, 20206702, 3);
INSERT INTO `orderinfo` VALUES (4, 20200645, 1);
INSERT INTO `orderinfo` VALUES (5, 20200711, 2);
INSERT INTO `orderinfo` VALUES (6, 20200811, 2);
INSERT INTO `orderinfo` VALUES (7, 20201422, 3);
INSERT INTO `orderinfo` VALUES (8, 20201688, 4);

SET FOREIGN_KEY_CHECKS = 1;

SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for ordersdetail
-- ----------------------------
DROP TABLE IF EXISTS `ordersdetail`;
CREATE TABLE `ordersdetail`  (
  `odid` int(0) NOT NULL AUTO_INCREMENT,
  `orderId` int(0) NULL DEFAULT NULL,
  `productId` int(0) NULL DEFAULT NULL,
  PRIMARY KEY (`odid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of ordersdetail
-- ----------------------------
INSERT INTO `ordersdetail` VALUES (1, 1, 1);
INSERT INTO `ordersdetail` VALUES (2, 1, 2);
INSERT INTO `ordersdetail` VALUES (3, 1, 3);
INSERT INTO `ordersdetail` VALUES (4, 2, 3);
INSERT INTO `ordersdetail` VALUES (5, 2, 1);
INSERT INTO `ordersdetail` VALUES (6, 3, 2);

SET FOREIGN_KEY_CHECKS = 1;

SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for product
-- ----------------------------
DROP TABLE IF EXISTS `product`;
CREATE TABLE `product`  (
  `pid` int(0) NOT NULL AUTO_INCREMENT,
  `pname` varchar(25) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `price` double NULL DEFAULT NULL,
  PRIMARY KEY (`pid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of product
-- ----------------------------
INSERT INTO `product` VALUES (1, 'JavaWeb', 128);
INSERT INTO `product` VALUES (2, 'C##', 138);
INSERT INTO `product` VALUES (3, 'Python', 132.35);

SET FOREIGN_KEY_CHECKS = 1;


分析表关系

在这里插入图片描述

在这里插入图片描述

所以在userinfo实体类添加 orderinfo的集合,

@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserInfo implements Serializable {
    private static final long serialVersionUID = 1L;

    private Integer uid;

    private String uname;

    private String upwd;

    private List<OrderInfo> orderInfo;

    
}

在orderinfo中添加orderdetail集合

@Data
@AllArgsConstructor
@NoArgsConstructor
public class OrderInfo implements Serializable {
    private static final long serialVersionUID = 1L;
    private Integer oid;
    private Integer ordernum;
    private Integer userid;
    //    一对一  一条订单 对应一条订单信息
  List  <OrdersDetail> ordersdetail;
//    //一条订单信息对商品    一对多
//    private List<Product>products;
    }

然后在orderdetail中添加productj集合属性,

@Data
@AllArgsConstructor
@NoArgsConstructor
public class OrdersDetail implements Serializable {
    private static final long serialVersionUID = 1L;
    private Integer odid;

    private Integer orderid;

    private Integer productid;
    //一条订单信息对商品    一对多
    private List<Product>products;


}

product类不做任何操作

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Product implements Serializable {
    private static final long serialVersionUID = 1L;
    private Integer pid;
    private String pname;
    private Double price;
    
}

接下来写mapper接口中的方法了;
简单写一个 一个用户下的所有订单信息;

从用户角度出发,所以 使用 userinfo的实体类作为入口

定义查询方法----->>>

import com.gavin.pojo.UserInfo;
import java.util.List;
public interface UserInfoMapper {
List<UserInfo> findUserOrder (Integer uid);

}

开始搞mapper.xm中的映射;

先写sql语句,然后在构造resultMap返回值

<?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.gavin.mapper.UserInfoMapper">
 <resultMap id="userRef"    type="userInfo">
...............看下面

 </resultMap>
 <select id="findUserOrder"   resultMap="userRef" parameterType="int">
        SELECT
        u.*,

        o.*,

        od.*,

        p.*

        FROM
        userinfo u
    inner JOIN orderinfo o ON u.uid = o.userid
        inner   JOIN ordersdetail od ON o.oid = od.orderid
        inner   JOIN product p ON od.productid = p.pid
        <where>
            u.uid = #{uid}
        </where>


    </select>
</mapper>

自定义映射----

  <resultMap id="userRef"    type="userInfo">
        
        <!--        用户表-->
        <id property="uid" column="uid"/>
        <result property="uname" column="uname"/>
        <result property="upwd" column="upwd"/>


        <!--订单表-->
        <collection property="orderInfo" ofType="orderInfo">
            <id column="oid" property="oid"/>
            <result column="ordernum" property="ordernum"/>
            <result column="userid" property="userid"/>
            <!--订单信息表-->
            <collection property="ordersdetail" ofType="ordersDetail">
                <id column="odid" property="odid"/>
                <result column="orderid" property="orderid"/>
                <result column="productid" property="productid"/>

                <collection property="products" ofType="product">
                    <id property="pid" column="pid"/>
                    <result property="pname" column="pname"/>
                    <result property="price" column="price"/>
                </collection>
            </collection>
        </collection>

    </resultMap>

测试结果----->>

在这里插入图片描述

异常解决----查询时遇到异常

  org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 3

查看异常原因—查询到多条数据,但是mybais要求只能显示一条,这说明方法的返回类型不匹配,尝试改为集合或者数组 然后测试,一般问题就是出现在这里;

自定义映射最主要的是嵌套的问题,以及实体类引入属性的问题

如果是非常复杂的映射,需要嵌套好几层;

使用MyBatis的延迟加载在一定程度上可以降低运行消耗并提高查询效率,MyBatis默认没有开启延迟加载,需要在核心配置文件mybatis-config.xml中的元素内进行配置,具体配置方式如下。

在这里插入图片描述

在映射文件中,MyBatis关联映射的<association元素和<collection元素中都已默认配置了延迟加载属性,即默认属性fetchType=“lazy”(属性fetchType="eager"表示立即加载),所以在配置文件中开启延迟加载后,无须在映射文件中再做配置。

mybatis分页实现

准备一个分页工具文件----

package com.gavin.util;

import com.gavin.pojo.UserInfo;

import java.util.List;

public class page {

    //    表数据
    private Integer dataCount;
    //每页显示多少条数据
    private Integer showData;
    //一共多少个页
    private Integer pageCount;
    //当前页
    private Integer pageIndex;

    //    当前页现实的集合信息
    private List<UserInfo> list;

    public page() {
    }

    public page(Integer dataCount, Integer showData, Integer pageCount, Integer pageIndex, List<UserInfo> list) {
        this.dataCount = dataCount;
        this.showData = showData;
        this.pageCount = pageCount;
        this.pageIndex = pageIndex;
        this.list = list;
    }

    public Integer getDataCount() {
        return dataCount;
    }

    public void setDataCount(Integer dataCount) {
        this.dataCount = dataCount;
    }

    public Integer getShowData() {
        return showData;
    }

    public void setShowData(Integer showData) {
        this.showData = showData;
    }

    public Integer getPageCount() {
//        总条数除以每页显示数据,能整除就取这个值,否则+1
        return this.dataCount % this.showData == 0 ? this.dataCount / this.showData : this.dataCount / this.showData + 1;

    }

/*    public void setPageCount(Integer pageCount) {
        this.pageCount = pageCount;
    }*/

    public Integer getPageIndex() {
        return pageIndex;
    }

    public void setPageIndex(Integer pageIndex) {
        this.pageIndex = pageIndex;
    }

    public List<UserInfo> getList() {
        return list;
    }

    public void setList(List<UserInfo> list) {
        this.list = list;
    }
}

测试后在这里插入图片描述

参数设置-----

查询所有记录数%每页显示的数,如果能整除,则 页数为记录数/每页显示数,否则加一,但是这里有一个坑,如果是数组或者集合,由于是下标为零开始,所以可以不用加1,需要灵活处理;

也不用这么麻烦,其实还有一个工具类----RowBounds用它就可以直接
直线分页;

    @Test
    public void Test() {

        UserInfoMapper mapper = sqlSession.getMapper(UserInfoMapper.class);


        RowBounds rowBounds= new RowBounds(0,2);
        List<UserInfo> userInfos = mapper.selectBypage(rowBounds);
        for (UserInfo u :
                userInfos) {
            System.out.println(u.getUid() + "--" + u.getUname());
        }

    }

源代码------>>>在这里

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

CodeMartain

祝:生活蒸蒸日上!

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

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

打赏作者

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

抵扣说明:

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

余额充值