多表之间的数据交互

其实一对一和一对多映射,在前面的配置中已经接触到,我没在日志里直接说明,是因为想要在之后写一篇总结日志(就是本篇),总结这些高级映射的配置。例如一对一查询在关联的嵌套结果集查询中就涉及到,一对多查询则在这个基础上再加上一个或多个嵌套结果集,它们可以是一个实体类,或者是一个集合。多对多查询稍微有点复杂,举个例子来说,一个商城管理系统中,一名顾客在一个购物清单中可以有多件商品,而一件商品可以被多名顾客选购,它们之间的关系是多对对。假设现在有这么一个需求,查询数据表库中,所有的顾客信息,包括它们的购物清单,以及清单里面的商品信息,怎么做?下面会说。

无论是一对一、一对多还是多对多查询,都涉及到不同表之间的数据交互,因为它们之间需要主外键关系来关联,例如顾客表和购物清单表都有顾客id属性,根据这个属性来查询不同顾客对应的购物清单,有了主外键关系后,就可以进行高级映射了。

事例模型

为了展示三种映射示例,首先建立一个商场管理系统模型,里面有三张数据表,分别是顾客表user,购物车(商品清单)表shoppingcart和商品表products。顾客和商品清单之间是一对多的关系,因为一名顾客可以多次进商场购物,每一次购物生成一个购物清单(购物车),而每一张购物清单只属于一名顾客,所以顾客表和购物车表之间是一对多的关系。   还有商品表product,因为一张购物清单中可以有多件商品,所以商品表和购物车表为一对多的关系。因为一名顾客可以购买多件商品,一件商品也可以属于多名顾客,所以顾客和商品又构成多对多关系。我们在测试时,可以建立基本的三张表,:顾客表、购物车表和商品表(当然最好多加几张表,例如商品评价表),它们之间的查询可以组合出一对一、一对多以及多对多的关系,理清各张表的关系后,就可以进行测试了。

一对一嵌套结果集查询

从上面的事例模型中可以看到,含一对一关系的两张表有,购物车表和用户表,要注意其中的顺序不要弄乱,购物车和顾客是一对一关系,但反过来不是,顾客和购物车是一对多的关系。在查询需求中,这种多表之间一对一查询的场景,典型的有:一对一嵌套结果集查询。举个例子,假如我们要查询购物车表中,某一张购物清单信息,以及对应的该名顾客的个人信息。有两种配置方式,或是说两种方法实现:

resultType结合查询包装类

实现上面的一对一嵌套结果集查询,如果使用resultType来配置SQL映射的话,因为涉及到两张表的交互,且最后只能映射到一个实体类中,所以需要新建一个查询包装类,来包括其中含有的两张表的结果集字段对应的属性(这句话表述的不好- -、)。也就是说,新创建一个查询包装类,里面既含有购物车表中的字段对应的属性,也含有用户表中字段对应的属性,这样才能接收结果集中包含的两个表里的数据。来看看这个查询包装类:

// 购物车表一对多查询包装类
public class ShoppingCartInstance extends ShoppingCart implements Serializable {    /*** */private static final long serialVersionUID = 1L;private String username; //顾客姓名private String gender; //性别private String province; //省会private String city; //城市public ShoppingCartInstance() {}public ShoppingCartInstance(int cartId, int userId, int productId, String productName, int number, double price, User user, String username, String gender, String province, String city) {super(cartId, userId, productId, productName, number, price);this.username = username;this.gender = gender;this.province = province;this.city = city;}// 省略get()和set()方法
}

我们的查询包装类采用继承的方式得到购物车实体类的所有属性,当然你也可以重新一个一个自己定义,这样来舍去一些不需要的属性。接着在查询包装类中追加顾客表中的属性,对应返回结果集中顾客数据表的字段。

数据包装类准备好后,接下来可以写我们的SQL语句和映射配置了:

<!-- 一对一查询:resultType实现 --><select id="queryShoppingCartInstance1" parameterType="int" resultType="ShoppingCartInstance">SELECTS.cartId, S.productName, S.userId, S.price, S.number,U.id, U.username, U.gender, U.province, U.cityFROM ShoppingCart S left outer join user U on S.userId = U.id WHERE S.cartId = #{id}</select>

SQL语句根据id查询出购物清单,并根据外键,顾客id关联另一张数据表user顾客表。

 public void TestAssociationQuery() throws IOException {SqlSession sqlSession = dataConn.getSqlSession();List<ShoppingCartInstance> resultList = sqlSession.selectList("queryShoppingCartInstance1", 2);StringBuffer result = new StringBuffer();double totalAmount;System.out.println("顾客姓名: " + resultList.get(0).getUsername());System.out.println("性别: " + resultList.get(0).getGender());System.out.println("商品清单:" + "\r\n");for(int i=0; i<resultList.size(); i++) {ShoppingCartInstance shoppingCartInstance = resultList.get(i);result.append("商品名:" + shoppingCartInstance.getProductName() + "\r\n");result.append("商品数量:" + shoppingCartInstance.getNumber() + "\r\n");totalAmount = (shoppingCartInstance.getPrice()*shoppingCartInstance.getNumber());result.append("商品总价:" + String.format("%.2f", totalAmount) + "\r\n");System.out.println(result.toString());result.setLength(0);}sqlSession.close();}

最后写个简单的测试用例,查询购物车id为2的商品清单信息,以及对应的顾客信息。

resultMap结合association标签

使用resultMap配置的话,灵活性就增强了许多,首先resultMap允许我们配置返回结果集中字段名和Java包装类中属性名的一一映射关系。且resultMap中提供的如association标签和collection标签,功能强大,可以配置多表之间的交互。还是上面的例子采用resultMap配置的话,在查询包装类中,我们可以直接追加一个User类的实例:

public class ShoppingCartInstance extends ShoppingCart implements Serializable { /*** */private static final long serialVersionUID = 1L;
//  private String username; //顾客姓名
//  private String gender; //性别
//  private String province; //省会
//  private String city; //城市private User user; //对应用户private Product products;public ShoppingCartInstance() {}public ShoppingCartInstance(int cartId, int userId, int productId, String productName, int number, double price, User user) {super(cartId, userId, productId, productName, number, price);
//      this.username = username;
//      this.gender = gender;
//      this.province = province;
//      this.city = city;this.user = user;}// 省略get()和set()方法
}

这个User类实例就是用来接收返回结果集中的顾客信息。接下来就配置它们的映射关系:

<resultMap type="shoppingCartInstance" id="shoppingCartResult"><id property="id" column="shoppingCart_id"/><result property="cartId" column="cart_id"/><result property="productName" column="product_name"/><result property="price" column="product_price"/><result property="number" column="product_number"/><result property="productId" column="product_id"/><association property="user" column="cartUser_id" javaType="com.mybatis.po.User" resultMap="userResult"/></resultMap><resultMap type="com.mybatis.po.User" id="userResult"><id property="id" column="user_id"/><!-- 配置User类中的映射关系 --><result property="username" column="user_name"/><result property="gender" column="user_gender"/><result property="province" column="user_province"/><result property="city" column="user_city"/></resultMap>

在查询包装类的映射关系配置resultMap中,type指明接收的实体类是shoppingCartInstance,下面除了配置各个数据库字段和实体类属性的映射外,还用assocaition标签配置关联的嵌套结果集,也就是查询包装类中的User类实例,并且指明User类的映射关系resultMap是引用外部的userResult(也就是下面那个)。

配置完resultMap,那SQL语句就很简单,把resultType修改为resultMap就可以了:

<!-- 一对多查询SQL语句 --><select id="queryShoppingCartInstance2" parameterType="int" resultMap="shoppingCartResult">SELECTS.cartId    as cart_id,S.productName    as product_name,S.userId    as cartUser_id,S.price  as product_price,S.number   as product_number,U.id  as user_id,U.username   as user_name,U.gender   as user_gender,U.province   as user_province,U.city as user_cityFROM ShoppingCart S left outer join user U on S.userId = U.id WHERE S.cartId = #{id}</select>

最后是测试用例:

​public void TestAssociationQuery() throws IOException {SqlSession sqlSession = dataConn.getSqlSession();List<ShoppingCartInstance> resultList = sqlSession.selectList("queryShoppingCartInstance2", 2);StringBuffer result = new StringBuffer();double totalAmount;System.out.println("顾客姓名: " + resultList.get(0).getUser().getUsername());System.out.println("性别: " + resultList.get(0).getUser().getGender());System.out.println("商品清单:" + "\r\n");for(int i=0; i<resultList.size(); i++) {ShoppingCartInstance shoppingCartInstance = resultList.get(i);result.append("商品名:" + shoppingCartInstance.getProductName() + "\r\n");result.append("商品数量:" + shoppingCartInstance.getNumber() + "\r\n");totalAmount = (shoppingCartInstance.getPrice()*shoppingCartInstance.getNumber());result.append("商品总价:" + String.format("%.2f", totalAmount) + "\r\n");System.out.println(result.toString());result.setLength(0);}sqlSession.close();}​

一对多查询

一对多查询,假如我们在上面例子的基础上,加上一个,查询出购物清单中每一件商品的一些商品信息,如顾客对件商品的评价,怎么做?

包装类和SQL映射配置

首先看查询包装类,既然是在上一个例子的基础上,增加查询顾客对商品的评价信息,那么只需要在原有的查询包装类中,加上一个结果集属性即可,因为一件商品可以被多名顾客做评价,所以商品的评价属性我们用一个集合List来表示:

// 一对多查询包装类
public class ShoppingCartInstance extends ShoppingCart implements Serializable {    /*** */private static final long serialVersionUID = 1L;private User user; //对应用户private List<ProductAppraise> productAppraise; //商品评价集合public ShoppingCartInstance() {}public ShoppingCartInstance(int cartId, int userId, int productId, String productName, int number, double price, User user, List<ProductAppraise> productAppraise) {super(cartId, userId, productId, productName, number, price);this.user = user;this.productAppraise = productAppraise;}   // 省略get()和set()方法
}

查询包装类定义好后,接下来到SQL语句和映射配置。因为有嵌套结果集和结合的缘故,很明显我们要用resultMap来配置映射关系:

<!-- 一对多映射配置 --><resultMap type="shoppingCartInstance" id="multiShoppingCartResult" extends="shoppingCartResult"><collection property="productAppraise" ofType="ProductAppraise"><!-- 主键 --><id column="id" property="id"/><result property="productId" column="productAppraise_id"/><result property="productScore" column="product_score"/><result property="userId" column="user_id"/></collection></resultMap>

因为我们的需求是在上面的查询基础上,增加查询商品的评价,所以映射配置我们只需继承上面的resultMap,然后增加一个嵌套集合配置,使用collection标签即可。

配置还没完,还有最重要的SQL语句:

<!-- 一对多查询SQL语句 --><select id="queryShoppingCartInstance2" parameterType="int" resultMap="shoppingCartResult">SELECTS.cartId    as cart_id,S.productName    as product_name,S.userId    as cartUser_id,S.price  as product_price,S.number   as product_number,U.id  as user_id,U.username   as user_name,U.gender   as user_gender,U.province   as user_province,U.city as user_cityFROM ShoppingCart S left outer join user U on S.userId = U.id WHERE S.cartId = #{id}</select>

SQL语句中,查询三张表中的数据,根据外键顾客id来关联顾客和对应的购物车,根据商品号productId来关联购物车中的商品和商品评价信息。

测试用例

最后是一对多查询的测试用例:

// 一对多查询测试用例
public void TestAssociationQuery() throws IOException {SqlSession sqlSession = dataConn.getSqlSession();List<ShoppingCartInstance> resultList = sqlSession.selectList("queryShoppingCartInstance4");StringBuffer result = new StringBuffer();double totalAmount;System.out.println("顾客姓名: " + resultList.get(0).getUser().getUsername());System.out.println("性别: " + resultList.get(0).getUser().getGender());System.out.println("商品清单:" + "\r\n");for(int i=0; i<resultList.size(); i++) {ShoppingCartInstance shoppingCartInstance = resultList.get(i);result.append("商品名:" + shoppingCartInstance.getProductName() + "\r\n");result.append("商品数量:" + shoppingCartInstance.getNumber() + "\r\n");totalAmount = (shoppingCartInstance.getPrice()*shoppingCartInstance.getNumber());result.append("商品总价:" + String.format("%.2f", totalAmount) + "\r\n");List<ProductAppraise> appraiseList = shoppingCartInstance.getProductAppraise();for(int j=0; j<appraiseList.size(); j++) {ProductAppraise productAppraise = appraiseList.get(j);System.out.println("商品评分:" + productAppraise.getProductScore());}System.out.println(result.toString());result.setLength(0);}sqlSession.close();}

测试用例中,每一次从结果集中拿到一个shoppingCartInstance对象后,第18行,都要从对象中再获取商品评价的集合,然后从商品评价集合中在获取商品评分,这里要注意一下。

测试结果:

多对多查询

还有更复杂的需求,就是要用到多对多查询,由前面事例模型可知,具有多对多关系的是顾客和商品之间,因为一名顾客可以购买多种商品,同一种商品也可以被多名顾客购买,这就形成了很明显的多对多关系。假如现在有这么一个需求,查询所有的顾客,以及这些顾客对应的商品清单,还有清单里所有商品的商品id信息。怎么做?还是先从查询包装类开始:

// 多对多查询包装类
public class ShoppingCartInstance extends ShoppingCart implements Serializable {    /*** */private static final long serialVersionUID = 1L;private User user; //对应用户private Product products;public ShoppingCartInstance() {}public ShoppingCartInstance(int cartId, int userId, int productId, String productName, int number, double price, User user, Product products) {super(cartId, userId, productId, productName, number, price);this.user = user;this.products = products;}// 省略get()和set()方法
}

为了简化示例,我们只查询商品信息中的商品id信息,再查询包装类中,我们在上面的示例基础上,移除商品评价集合,追加一个商品结果集实体类实例对象products,用来接收商品信息。

多个association标签

接下来到SQL映射配置:

    <resultMap type="shoppingCartInstance" id="shoppingCartResult"><id property="id" column="shoppingCart_id"/><result property="cartId" column="cart_id"/><result property="productName" column="product_name"/><result property="price" column="product_price"/><result property="number" column="product_number"/><result property="productId" column="product_id"/><association property="user" column="cartUser_id" javaType="com.mybatis.po.User" resultMap="userResult"/><association property="products" column="cartUser_id" javaType="com.mybatis.po.Product"><result property="productId" column="products_id"/></association></resultMap><resultMap type="com.mybatis.po.User" id="userResult"><id property="id" column="user_id"/><!-- 配置User类中的映射关系 --><result property="username" column="user_name"/><result property="gender" column="user_gender"/><result property="province" column="user_province"/><result property="city" column="user_city"/></resultMap>

在多对多映射配置中,注意用了两个association标签,这是可以的,因为我们的查询包装类中,嵌套了两个实体类,一个顾客类和一个商品类。两个association标签配置有一点不同就是,顾客映射关系配置使用了外部的resultMap,而商品信息映射关系配置直接在association标签内配置,两种方法都可以。SQL映射配置完后,到SQL语句:

<!-- 多对多查询SQL示例语句 --><select id="queryShoppingCartInstance4" resultMap="shoppingCartResult">SELECTS.cartId as cart_id,S.productName    as product_name,S.userId    as cartUser_id,S.price  as product_price,S.number   as product_number,S.productId as product_id,U.id    as user_id,U.username   as user_name,U.gender   as user_gender,U.province   as user_province,U.city as user_city,P.productId as products_idFROM Products P, ShoppingCart S, user U WHERE S.userId = U.id AND S.productId = P.productId</select>

这个比较简单,注意外键的关联即可,通过顾客id关联顾客和顾客的商品清单,通过商品id关联购物清单中的商品和商品信息。最后是测试用例:

// 多对多测试用例
public void TestAssociationQuery() throws IOException {SqlSession sqlSession = dataConn.getSqlSession();List<ShoppingCartInstance> resultList = sqlSession.selectList("queryShoppingCartInstance4");StringBuffer result = new StringBuffer();double totalAmount;for(int i=0; i<resultList.size(); i++) {ShoppingCartInstance shoppingCartInstance = resultList.get(i);result.append("顾客姓名: " + shoppingCartInstance.getUser().getUsername() + "\r\n");result.append("性别: " + shoppingCartInstance.getUser().getGender() + "\r\n");result.append("商品id:" + shoppingCartInstance.getProducts().getProductId() + "\r\n");result.append("商品名:" + shoppingCartInstance.getProductName() + "\r\n");result.append("商品数量:" + shoppingCartInstance.getNumber() + "\r\n");totalAmount = (shoppingCartInstance.getPrice()*shoppingCartInstance.getNumber());result.append("商品总价:" + String.format("%.2f", totalAmount) + "\r\n");System.out.println(result.toString());result.setLength(0);}sqlSession.close();}

也比较简洁明了,因为所有信息都在一个查询包装类中,我们可以一一拿出来:

完整实现已上传GitHub:

https://github.com/justinzengtm/SSM-Framework

高级映射(一):一对一、一对多,多对多查询总结相关推荐

  1. gorm一对一 一对多 多对多查询案例

    Student -- IDCard -- Class -- Teacher

  2. 【Mybatis高级映射】一对一映射、一对多映射、多对多映射

    前言 当我们学习heribnate的时候,也就是SSH框架的网上商城的时候,我们就学习过它对应的高级映射,一对一映射,一对多映射,多对多映射.对于SSM的Mybatis来说,肯定也是差不多的.既然开了 ...

  3. 7. MyBatis多表查询 - 一对一 - 一对多 - 多对多

    7. MyBatis多表查询 - 一对一 - 一对多 - 多对多 前言 在前面的篇章,我们已经熟悉了单表查询,下面我们来看看如何进行 多表查询. 数据准备 create database if not ...

  4. mybatis的一对一 一对多 多对多

    mybatis的一对一 一对多 多对多 1.表 2.建表语句 order_t表 CREATE TABLE `order_t` ( `id` int(11) NOT NULL, `user_id` in ...

  5. SQLAlchemy_定义(一对一/一对多/多对多)关系

    SQLAlchemy_定义(一对一/一对多/多对多)关系 目录 Basic Relationship Patterns One To Many One To One Many To Many Basi ...

  6. Python进阶----表与表之间的关系(一对一,一对多,多对多),增删改查操作

    Python进阶----表与表之间的关系(一对一,一对多,多对多),增删改查操作,单表查询,多表查询 一丶表与表之间的关系 背景: ​ ​ ​  ​ ​ 由于如果只使用一张表存储所有的数据,就会操作数 ...

  7. Mybatis-Plus用纯注解完成一对多多对多查询

    Mybatis-Plus用纯注解搞定一对多&多对多查询 业务中很常见的用户-角色就属于典型的多对多关系. 假设我们需要将用户信息(包括了用户对应的角色信息)查询出来 多对多 数据表结构 use ...

  8. Mybatis(四) 高级映射,一对一,一对多,多对多映射

    天气甚好,怎能不学习? 一.单向和双向 包括一对一,一对多,多对多这三种情况,但是每一种又分为单向和双向,在hibernate中我们就详细解析过这单向和双向是啥意思,在这里,在重复一遍,就拿一对多这种 ...

  9. Mybatis中的关系映射(一对一,一对多,多对多)

    在网上寻了很久,大多数讲关系性的文章都是大篇幅的去将表照搬上来,本来就很生硬,此文就不在讲述关系性映射的具体实现,转而从浅层来讲讲其概念性. 1.1 关联关系概述 在关系型数据库中,多表之间存在着三种 ...

  10. JAVA日记之mybatis-3一对一,一对多,多对多xml与注解配置 ----喝最烈的酒.

    1.Mybatis多表查询 1.1 一对一查询 1.1.1 一对一查询的模型 用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户 一对一查询的需求:查询一个订单,与此同时查询出该订单 ...

最新文章

  1. Win2003下Exchange2003部署图解之二
  2. VUE工程跨域的配置
  3. 各互联网公司面试题整理
  4. js计算两个日期的时间差
  5. [转载]C#中各种计时器
  6. 原型模式 —— Java的赋值、浅克隆和深度克隆的区别
  7. 矩阵学习摘记,欢迎指正
  8. ppt变成了图片不能编辑文字怎么办_谁说水印一定要去掉?用到PPT里贼好看好吗!...
  9. 容器精华问答 | 虚拟机和容器的区别是什么?
  10. Android的面孔_Actiyity
  11. mysql 数字序列_MySQL中的数字序列
  12. 工业品器械设备怎么做宣传和推广?
  13. VS F5自动编译 F5不自动编译
  14. java ssm基于springboot的设备巡检系统
  15. Deepin系统安装摄像头驱动
  16. java-小学期小小项目-通讯录管理系统
  17. glibc 知:手册05:字符串和数组
  18. 【数据分析】基于新闻文本数据分析
  19. 系统集成项目管理工程师高频考点(第八章)
  20. git pages部署静态页面,可以免费发布简历之类的静态网页。

热门文章

  1. unittest输出测试报告
  2. 人——Web3的新平台
  3. onenote同时移动图片和绘图
  4. 大型网站之存储瓶颈(数据库的垂直拆分)
  5. 大型网站系统架构的演化
  6. UltraEdit连接linux中文乱码,UltraEdit显示中文乱码的解决办法
  7. Android Wifi P2P 入门
  8. 数学建模 ————统计问题之预测(一)
  9. [BZOJ]4864: [BeiJing 2017 Wc]神秘物质
  10. nginx架构(修改版)