MyBatis进阶四:Mybatis对象关联查询;(【一对多】&【多对一】)

(1)对本篇博客的内容有一个疑问,在写一个表的实体类后,,,,随着后续的开发,需要随时修改这个实体类???所以,这就涉及到项目的功能一开始就要规划清楚,不要随时新增或者修改需求。。。。。这样才能做好规划,进而不要任意修改已经写好的代码。。。。说多了都是泪,经验太少~~~

(2)数据库表的设计原则,设计能力有待加强。

(3)【一对多】的情况下,【多的那个表】就是【一的那个表】的“附庸”?

目录

零:为什么需要本篇博客的东西

(1)原因1:这个例子是自己的主观观点(以后,随着经验加深,回头看的时候大概率会觉得略幼稚~~~)

(2)原因2:对象关联查询可以简化工作量

一:【多表关联查询】和【多表级联查询(或称对象关联查询)】的区别;【一对一】,【一对多】,【多对多】的表关系列举;

预先说明:两张表的关系 

二:案例1:【一对多】表关系下的案例

首先,因为涉及到t_goods_detail表,所以添加GoodsDetail类,添加goods_detail.xml

然后,在Goods类中,添加【List goodsDetails】→在对象层面上完成了Goods对象和GoodsDetail对象的关联

然后,为了指明【List goodsDetails】的数据来源,需要在goods.xml中进行对象关联的描述

最后,就是实际调用

二:案例2:【多对一】表关系下的案例

首先,在GoodsDetail中添加Goods类型的对象→在对象层面上完成GoodsDetail对象和Goods对象的关联

然后,为了指明GoodsDetail类对象中的goods的数据来源,需要在goods_detail.xml中进行对象关联的描述

最后,就是实际调用

一个小问题


零:为什么需要本篇博客的东西

(1)原因1:这个例子是自己的主观观点(以后,随着经验加深,回头看的时候大概率会觉得略幼稚~~~)

一个例子说明:

如下:t_goods表:(在实际中,会有Goods实体类与之对应)

goods_id title current_price
768 莫斯利安 78
769 安慕希 67

 

t_goods_detail表:(在实际中,会有GoodsDetail实体类与之对应)

gd_id goods_id gd_detail
9001 768 莫斯利安描述1
9002 768 莫斯利安描述2
9003 769 安慕希描述1
9004 769 安慕希描述2

 

比如要查询【莫斯利安的信息和它的描述】如果采用以前的多表关联查询的策略,也就是只需要一条SQL语句就完成查询,其SQL语句大致是:

SELECT *
FROM t_goods g LEFT JOIN t_goods_detail d ON g.goods_id=d.goods_id
WHERE g.goods_id=768;

这背后其实是笛卡尔积在支撑啦,其查询的结果如下:很简单哈,这也是以前经常遇到的,比较熟悉的东西;

 

goods_id title current_price gd_id goods_id(1) gd_detail
768 莫斯利安 78 9001 768 莫斯利安描述1
768 莫斯利安 78 9002 768 莫斯利安描述2

 

可以发现,这个查询结果的的字段包含了两张表的字段,,,,所以,单纯用Goods实体类对象或者GoodsDetail实体类对象是不能承载这个数据的。

为了能承载存储这些数据,自然可以考虑使用以前遇到过的GoodsDAO的策略去处理(GoodsDao的例子见MyBatis入门八:多表关联查询二:ResultMap结果映射;);但是,如果继续使用MyBatis入门八:多表关联查询二:ResultMap结果映射;中的那种传统的DAO策略,就会出现这种情况:【为了能保存上面的查询结果,需要用两个GoodsDAO对象去承载存储】,而这样是不太好的。

为了,对上面DAO策略的一点小小改进,就需要抛弃上面的【多表关联查询】策略,而是采用本篇博客的中的【多表级联查询】(或称对象关联查询)的东西了。

即基本思想就是:即,因为t_goods表和t_goods_detail表是【一对多】的关系,所以,Goods实体类就这样写。其结果就是【为了能保存上面的查询结果,只需要一个Goods对象去存储就好了】。

public class Goods {
    private Integer goodsId;
    private String title;
    private Float currentPrice;
    private List goodsDetails;
}

当然,为了实现上面功能,需要在Mybatis中设置一下,以实现对象关联查询或称【多表级联查询】。

 

(2)原因2:对象关联查询可以简化工作量

通过【一对多】或者【多对一】关联查询,可以降低开发的工作量,让这些繁琐的SQL语句,让mybatis自动帮我们执行;所有的SQL放在xml中,由mybatis进行自动的管理与执行,降低了出错的风险,,提高开发效率。


 

一:【多表关联查询】和【多表级联查询(或称对象关联查询)】的区别;【一对一】,【一对多】,【多对多】的表关系列举;

本篇博客主要内容是多表级联查询,前面MyBatis入门七:多表关联查询一中介绍的是多表关联查询。 多表关联查询和多表级联查询不同。

       多表关联查询:两个表通过主外键,在一条SQL语句中,完成所有数据的查询提取工作;

       多表级联查询(或称对象关联查询):通常是指,通过一个对象来获取与它关联的另外一个对象,执行的SQL语句是分为多条的;

MySQL是关系型数据库,有主键、外键;数据的关系也有【一对一】,【一对多】,【多对多】。这些表和表之间的关系:

MyBatis进阶四:Mybatis对象关联查询;(【一对多】&【多对一】)_第1张图片

说明:(核心是:表的设计原则)

(1)【班级&学生】一个班级有多个学生,一个多学生只能属于一个班级,所以【班级&学生】是【一对多】的关系,【班级是一】,【学生是多】;;;;数据表设计时,在班级表和学生表中,,,,学生表中的记录应该持有班级表中的主键;

(2)【档案&学生】是【一对一】的关系;数据表设计时,那么档案表和学生表,就需要通过主键来关联。。。。即学生表id=1的那条记录和档案表id=1的那条记录应该是同一个人。

(3)【课程&学生】是【多对多】的关系;数据表设计时,【多对多】的时候,需要单独抽象出一张中间表,在这张中间表中持有学生和课程的编号,通控学生表与中间表关联,通过课程表与中间表关联,,,这三张表就构成一个标准的多对多关系。

本篇博客的实际案例主要是【一对多】和【多对一】。 


预先说明:两张表的关系 

 比如案例:因为【商品】和【商品图片】是【一对多】的关系;所以,在设计商品表和商品详情表的时候,就需要让商品图片表持有商品表的主键啦;

MyBatis进阶四:Mybatis对象关联查询;(【一对多】&【多对一】)_第2张图片

MyBatis进阶四:Mybatis对象关联查询;(【一对多】&【多对一】)_第3张图片


 

二:案例1:【一对多】表关系下的案例

需求:根据商品信息查找商品详情信息;

解决方案:因为这是典型的【一对多】的情况,所以可以利用mybatis的【对象关联查询】来获取某商品下的所有详情; 

 

首先,因为涉及到t_goods_detail表,所以添加GoodsDetail类,添加goods_detail.xml

MyBatis进阶四:Mybatis对象关联查询;(【一对多】&【多对一】)_第4张图片

GoodsDetail类:

package com.imooc.mybatis.entity;

public class GoodsDetail {
    private Integer gdId; //商品详情图片表主键
    private Integer goodsId;  // 商品id,是商品表的主键
    private String gdPicUrl;  //图片url
    private Integer gdOrder; //图片显示顺序

    public Integer getGdId() {
        return gdId;
    }

    public void setGdId(Integer gdId) {
        this.gdId = gdId;
    }

    public Integer getGoodsId() {
        return goodsId;
    }

    public void setGoodsId(Integer goodsId) {
        this.goodsId = goodsId;
    }

    public String getGdPicUrl() {
        return gdPicUrl;
    }

    public void setGdPicUrl(String gdPicUrl) {
        this.gdPicUrl = gdPicUrl;
    }

    public Integer getGdOrder() {
        return gdOrder;
    }

    public void setGdOrder(Integer gdOrder) {
        this.gdOrder = gdOrder;
    }
}

MyBatis进阶四:Mybatis对象关联查询;(【一对多】&【多对一】)_第5张图片

goods_detail.xml:




    

说明:(1)其核心就是,根据传入的商品id,来查询商品的详情;(2)待会,进行对象关联的时候,需要使用到这个SQL;

然后,要记得早mybatis-config.xml中配置一下goods_detail.xml;

MyBatis进阶四:Mybatis对象关联查询;(【一对多】&【多对一】)_第6张图片

 

 

然后,在Goods类中,添加【List goodsDetails】→在对象层面上完成了Goods对象和GoodsDetail对象的关联

因为是【一对多】的关系,即【t_goods】表示一,【t_goods_detail】是多;;;即Goods类是一,GoodsDetail类是多;

MyBatis进阶四:Mybatis对象关联查询;(【一对多】&【多对一】)_第7张图片

这个也是前面遇到的DAO啦;或者可以认为是强化班的DAO~~~。至此,在对象结构层面上,已经完成了Goods对象和GoodsDetail对象的关联。

但是,如果获取List goodsDetails数据?为此,需要在goods.xml(即“一”的一方)对这个List goodsDetails进行说明;

 

然后,为了指明【List goodsDetails】的数据来源,需要在goods.xml中进行对象关联的描述

MyBatis进阶四:Mybatis对象关联查询;(【一对多】&【多对一】)_第8张图片

 

最后,就是实际调用

package com.imooc.mybatis;

import com.imooc.mybatis.dto.GoodsDTO;
import com.imooc.mybatis.entity.Goods;
import com.imooc.mybatis.entity.Student;
import com.imooc.mybatis.utils.MyBatisUtils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

import java.io.IOException;
import java.io.Reader;
import java.sql.Connection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * JUnit单元测试类
 */
public class MyBatisTestor {

    @Test
    public void testOneToMany() throws Exception {
        SqlSession session = null;
        try {
            session = MyBatisUtils.openSession();
            List list = session.selectList("goods.selectOneToMany");
            for (Goods goods:list){
                System.out.println(goods.getTitle()+":"+goods.getGoodsDetails().size());
            }
        } catch (Exception e) {
            throw e;
        }finally {
            MyBatisUtils.closeSession(session);
        }
    }
}

运行结果:

MyBatis进阶四:Mybatis对象关联查询;(【一对多】&【多对一】)_第9张图片

MyBatis进阶四:Mybatis对象关联查询;(【一对多】&【多对一】)_第10张图片

OK,能懂哈~~~不难啦。


 

二:案例2:【多对一】表关系下的案例

需求:根据商品详细信息获取商品信息;

解决方案:因为这是典型的【多对一】的情况,所以可以利用mybatis的【对象关联查询】。 

因为在【多对一】关系中,商品详情是多,商品信息是一,所以一个商品详情信息肯定只对应一条商品信息。(即t_goods_detail表的一条记录,肯定只对应一条t_goods表中的记录啦)

 

首先,在GoodsDetail中添加Goods类型的对象→在对象层面上完成GoodsDetail对象和Goods对象的关联

MyBatis进阶四:Mybatis对象关联查询;(【一对多】&【多对一】)_第11张图片

即【多的一方】(GoodsDetail)要关联到【一的一方】(Goods),只需要持有【一的一方】的实体就可以了。。。。即,如上,只需要在GoodsDetail实体类中,增加一个Goods类对象属性可以了;

 

然后,为了指明GoodsDetail类对象中的goods的数据来源,需要在goods_detail.xml中进行对象关联的描述

MyBatis进阶四:Mybatis对象关联查询;(【一对多】&【多对一】)_第12张图片

 

最后,就是实际调用

package com.imooc.mybatis;

import com.imooc.mybatis.dto.GoodsDTO;
import com.imooc.mybatis.entity.Goods;
import com.imooc.mybatis.entity.GoodsDetail;
import com.imooc.mybatis.entity.Student;
import com.imooc.mybatis.utils.MyBatisUtils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

import java.io.IOException;
import java.io.Reader;
import java.sql.Connection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * JUnit单元测试类
 */
public class MyBatisTestor {

    @Test
    public void testManyToOne() throws Exception {
        SqlSession session = null;
        try {
            session = MyBatisUtils.openSession();
            List list = session.selectList("goodsDetail.selectManyToOne");
            for (GoodsDetail goodsDetail:list){
                System.out.println(goodsDetail.getGdPicUrl()+":"+goodsDetail.getGoods().getTitle());
            }
        } catch (Exception e) {
            throw e;
        }finally {
            MyBatisUtils.closeSession(session);
        }
    }
}

运行结果:这个执行过程的说明,就不再啰嗦了。

MyBatis进阶四:Mybatis对象关联查询;(【一对多】&【多对一】)_第13张图片

 

一个小问题

MyBatis进阶四:Mybatis对象关联查询;(【一对多】&【多对一】)_第14张图片

为了解决这个问题,可以在resultMap中手动指定一下啦:

MyBatis进阶四:Mybatis对象关联查询;(【一对多】&【多对一】)_第15张图片

MyBatis进阶四:Mybatis对象关联查询;(【一对多】&【多对一】)_第16张图片


 

通过【一对多】或者【多对一】关联查询,可以降低开发的工作量,让这些繁琐的SQL语句,让mybatis自动帮我们执行;所有的SQL放在xml中,由mybatis进行自动的管理与执行,降低了出错的风险,,提高开发效率。

你可能感兴趣的:(Mybatis)