(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<GoodsDetail> goodsDetails;
}
当然,为了实现上面功能,需要在Mybatis中设置一下,以实现对象关联查询或称【 多表 级联查询】。
(2)原因2:对象关联查询可以简化工作量
通过【一对多】或者【多对一】关联查询,可以降低开发的工作量,让这些繁琐的SQL语句,让mybatis自动帮我们执行;所有的SQL放在xml中,由mybatis进行自动的管理与执行,降低了出错的风险,,提高开发效率。
一:【多表关联查询】和【多表级联查询(或称对象关联查询)】的区别;【一对一】,【一对多】,【多对多】的表关系列举;
本篇博客主要内容是多表级联查询,前面MyBatis入门七:多表关联查询一中介绍的是多表关联查询。 多表关联查询和多表级联查询不同。
多表关联查询:两个表通过主外键,在一条SQL语句中,完成所有数据的查询提取工作;
多表级联查询(或称对象关联查询):通常是指,通过一个对象来获取与它关联的另外一个对象,执行的SQL语句是分为多条的;
MySQL是关系型数据库,有主键、外键;数据的关系也有【一对一】,【一对多】,【多对多】。这些表和表之间的关系:
说明:(核心是:表的设计原则)
(1)【班级&学生】一个班级有多个学生,一个多学生只能属于一个班级,所以【班级&学生】是【一对多】的关系,【班级是一】,【学生是多】;;;;数据表设计时,在班级表和学生表中,,,,学生表中的记录应该持有班级表中的主键;
(2)【档案&学生】是【一对一】的关系;数据表设计时,那么档案表和学生表,就需要通过主键来关联。。。。即学生表id=1的那条记录和档案表id=1的那条记录应该是同一个人。
(3)【课程&学生】是【多对多】的关系;数据表设计时,【多对多】的时候,需要单独抽象出一张中间表,在这张中间表中持有学生和课程的编号,通控学生表与中间表关联,通过课程表与中间表关联,,,这三张表就构成一个标准的多对多关系。
本篇博客的实际案例主要是【一对多】和【多对一】。
预先说明:两张表的关系
比如案例:因为【商品】和【商品图片】是【一对多】的关系;所以,在设计商品表和商品详情表的时候,就需要让商品图片表持有商品表的主键啦;
二:案例1:【一对多】表关系下的案例
需求: 根据商品信息查找商品详情信息;
解决方案: 因为这是典型的【一对多】的情况,所以可以利用mybatis的【对象关联查询】来获取某商品下的所有详情;
首先,因为涉及到t_goods_detail表,所以添加GoodsDetail类,添加goods_detail.xml
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;
}
}
goods_detail.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="goodsDetail">
<select id="selectByGoodsId" parameterType="Integer" resultType="com.imooc.mybatis.entity.GoodsDetail">
select * from t_goods_detail where goods_id = #{value}
</select>
</mapper>
说明:(1)其核心就是,根据传入的商品id,来查询商品的详情;(2)待会,进行对象关联的时候,需要使用到这个SQL;
然后,要记得早mybatis-config.xml中配置一下goods_detail.xml;
然后,在Goods类中,添加【List
goodsDetails】→在对象层面上完成了Goods对象和GoodsDetail对象的关联
因为是【一对多】的关系,即【t_goods】表示一,【t_goods_detail】是多;;;即Goods类是一,GoodsDetail类是多;
这个也是前面遇到的DAO啦;或者可以认为是强化班的DAO~~~。至此,在对象结构层面上,已经完成了Goods对象和GoodsDetail对象的关联。
但是,如果获取List
然后,为了指明【List goodsDetails】的数据来源,需要在goods.xml中进行对象关联的描述
最后,就是实际调用
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<Goods> 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);
}
}
}
运行结果:
OK,能懂哈~~~不难啦。
二:案例2:【多对一】表关系下的案例
需求: 根据商品详细信息获取商品信息;
解决方案: 因为这是典型的【多对一】的情况,所以可以利用mybatis的【对象关联查询】。
因为在【多对一】关系中,商品详情是多,商品信息是一,所以一个商品详情信息肯定只对应一条商品信息。(即t_goods_detail表的一条记录,肯定只对应一条t_goods表中的记录啦)
首先,在GoodsDetail中添加Goods类型的对象→在对象层面上完成GoodsDetail对象和Goods对象的关联
即【多的一方】(GoodsDetail)要关联到【一的一方】(Goods),只需要持有【一的一方】的实体就可以了。。。。即,如上,只需要在GoodsDetail实体类中,增加一个Goods类对象属性可以了;
然后,为了指明GoodsDetail类对象中的goods的数据来源,需要在goods_detail.xml中进行对象关联的描述
最后,就是实际调用
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<GoodsDetail> 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);
}
}
}
运行结果:这个执行过程的说明,就不再啰嗦了。
一个小问题
为了解决这个问题,可以在resultMap中手动指定一下啦:
通过【一对多】或者【多对一】关联查询,可以降低开发的工作量,让这些繁琐的SQL语句,让mybatis自动帮我们执行;所有的SQL放在xml中,由mybatis进行自动的管理与执行,降低了出错的风险,,提高开发效率。