高级映射(一):一对一、一对多,多对多查询总结
多表之间的数据交互
其实一对一和一对多映射,在前面的配置中已经接触到,我没在日志里直接说明,是因为想要在之后写一篇总结日志(就是本篇),总结这些高级映射的配置。例如一对一查询在关联的嵌套结果集查询中就涉及到,一对多查询则在这个基础上再加上一个或多个嵌套结果集,它们可以是一个实体类,或者是一个集合。多对多查询稍微有点复杂,举个例子来说,一个商城管理系统中,一名顾客在一个购物清单中可以有多件商品,而一件商品可以被多名顾客选购,它们之间的关系是多对对。假设现在有这么一个需求,查询数据表库中,所有的顾客信息,包括它们的购物清单,以及清单里面的商品信息,怎么做?下面会说。
无论是一对一、一对多还是多对多查询,都涉及到不同表之间的数据交互,因为它们之间需要主外键关系来关联,例如顾客表和购物清单表都有顾客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
高级映射(一):一对一、一对多,多对多查询总结相关推荐
- gorm一对一 一对多 多对多查询案例
Student -- IDCard -- Class -- Teacher
- 【Mybatis高级映射】一对一映射、一对多映射、多对多映射
前言 当我们学习heribnate的时候,也就是SSH框架的网上商城的时候,我们就学习过它对应的高级映射,一对一映射,一对多映射,多对多映射.对于SSM的Mybatis来说,肯定也是差不多的.既然开了 ...
- 7. MyBatis多表查询 - 一对一 - 一对多 - 多对多
7. MyBatis多表查询 - 一对一 - 一对多 - 多对多 前言 在前面的篇章,我们已经熟悉了单表查询,下面我们来看看如何进行 多表查询. 数据准备 create database if not ...
- mybatis的一对一 一对多 多对多
mybatis的一对一 一对多 多对多 1.表 2.建表语句 order_t表 CREATE TABLE `order_t` ( `id` int(11) NOT NULL, `user_id` in ...
- SQLAlchemy_定义(一对一/一对多/多对多)关系
SQLAlchemy_定义(一对一/一对多/多对多)关系 目录 Basic Relationship Patterns One To Many One To One Many To Many Basi ...
- Python进阶----表与表之间的关系(一对一,一对多,多对多),增删改查操作
Python进阶----表与表之间的关系(一对一,一对多,多对多),增删改查操作,单表查询,多表查询 一丶表与表之间的关系 背景: 由于如果只使用一张表存储所有的数据,就会操作数 ...
- Mybatis-Plus用纯注解完成一对多多对多查询
Mybatis-Plus用纯注解搞定一对多&多对多查询 业务中很常见的用户-角色就属于典型的多对多关系. 假设我们需要将用户信息(包括了用户对应的角色信息)查询出来 多对多 数据表结构 use ...
- Mybatis(四) 高级映射,一对一,一对多,多对多映射
天气甚好,怎能不学习? 一.单向和双向 包括一对一,一对多,多对多这三种情况,但是每一种又分为单向和双向,在hibernate中我们就详细解析过这单向和双向是啥意思,在这里,在重复一遍,就拿一对多这种 ...
- Mybatis中的关系映射(一对一,一对多,多对多)
在网上寻了很久,大多数讲关系性的文章都是大篇幅的去将表照搬上来,本来就很生硬,此文就不在讲述关系性映射的具体实现,转而从浅层来讲讲其概念性. 1.1 关联关系概述 在关系型数据库中,多表之间存在着三种 ...
- JAVA日记之mybatis-3一对一,一对多,多对多xml与注解配置 ----喝最烈的酒.
1.Mybatis多表查询 1.1 一对一查询 1.1.1 一对一查询的模型 用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户 一对一查询的需求:查询一个订单,与此同时查询出该订单 ...
最新文章
- Win2003下Exchange2003部署图解之二
- VUE工程跨域的配置
- 各互联网公司面试题整理
- js计算两个日期的时间差
- [转载]C#中各种计时器
- 原型模式 —— Java的赋值、浅克隆和深度克隆的区别
- 矩阵学习摘记,欢迎指正
- ppt变成了图片不能编辑文字怎么办_谁说水印一定要去掉?用到PPT里贼好看好吗!...
- 容器精华问答 | 虚拟机和容器的区别是什么?
- Android的面孔_Actiyity
- mysql 数字序列_MySQL中的数字序列
- 工业品器械设备怎么做宣传和推广?
- VS F5自动编译 F5不自动编译
- java ssm基于springboot的设备巡检系统
- Deepin系统安装摄像头驱动
- java-小学期小小项目-通讯录管理系统
- glibc 知:手册05:字符串和数组
- 【数据分析】基于新闻文本数据分析
- 系统集成项目管理工程师高频考点(第八章)
- git pages部署静态页面,可以免费发布简历之类的静态网页。