一. 关系型数据库表关系回顾:
1. 一对一: 丈夫表和妻子表是典型的一对一关系;
RDB中的实现方式: 分别创建丈夫表和妻子表, 将对方表主键设为外键, 因为主键的唯一性, 保证了一对一关系;
2. 一对多: 公司和员工一般是一对多关系; (注意: 其实一对多表反过来看是一对一关系, 即一个员工只属于一个公司)
RDB中的实现方式: 分别创建公司.表和员工表, 将公司表主键设置为员工表外键;
3. 多对多: 订单和产品是典型的多对多关系;
RDB中实现方式: 因为RDB中表关系只能靠外键实现, 实际单一外键只能实现一对多关系, 故需要创建中间表, 以外键的 "乘积" 来
实现多对多的关系, 如下图:
其中中间表取两表主键为联合主键, 并分别作为关联两表的外键;
二. 原生MyBatis中的查表和结果封装:
1. 查询: 查询时, 查询参数必须封装为单个参数, 可以是基本数据类型也可以是POJO/ 数组/集合等 (不包括Map);
2. 返回值: 查询的单条结果只能封装到单个对象中, 当多条数据时自动作风扎UN个到List中, 故无论需要什么数据, 查询后单条结果都要进行封装;
三. MyBatis不同表关系的查询方法:
在Java中, 我们将每张表的字段作为 POJO 的成员属性, 创建不同的POJO, 此时可以体现:
1. 一对一关系查询: 查询某男人及其妻子的全部信息:
POJO: Husband类 Wife类
需求引出问题: 查询双方所有信息, 需要进行二表联查, 查询结果无法直接封装在Hunband或者Wife对象中;
问题解决:
方式1: 新建类Hunband_Wife, 封装二表联查后的所有属性, 此类并不具有实际意义, 仅为封装数据而存在, 违背OO思想, 不推荐;
方式2: 实际妻子表的主键id值在丈夫表中仅是一个映射 ( 理解为操作系统桌面快捷方式, 实际文件并未放到桌面, 当我们找到桌面对应文件夹时实际其中并不直接包含相应文件) , 我们需要将真实的Wife对象封装到Hunband对象中, 故在Hunband类中新增Wife实体作为成员属性 (同样,Wife类中新增Hunband实体作为成员属性), 如下图:
查询结果的多层封装需要用到MyBatis映射文件中的 resultMap 标签中的 association 子标签, 该标签可以将多表查询的一部分封装成实体成员属性对象 (association子标签可以有多个,即类中可以定义多个实体成员属性 ), 用法如下:
<resultMap id="mapOfFindAll" type="com.wen.domain.Hunband">
<id property="hid" column="hid"></id>
<result property="hname" column="hname"></result>
<result property="wid" column="wid"></result>
<association property="wife" javaType="com.wen.domain.Wife">
<id property="wid" column="wid"></id>
<result property="wname" column="wname"></result>
<result property="hid" column="hid"></result>
</association>
</resultMap>
2. 一对多关系查询: 查询某企业及其所有员工的信息
POJO: Company类, Staff类
需求引出问题: 查询该公司的所有员工,属于一对多关系,无法直接封装在实体类中, 也无法直接定义实体成员属性;
问题解决: 此时应该定义一个集合作为Company成员属性, 集合泛型为Staff, 即实现一个公司对应多个员工的目的:
但是,数据库二表联查结果表是二维表结构, 数据条数等于员工数, 需要对公司信息进行去重, 并将所有员工信息封装后放入List, 此时用到MyBatis映射文件中的 resultMap 标签中的 collection 子标签, 用法如下:
<resultMap id="mapOfFindAll" type="com.wen.domain.Company">
<id property="cid" column="cid"></id>
<result property="cname" column="cname"></result>
<result property="caddress" column="caddress"></result>
<collection property="staffList" ofType="list" javaType="com.wen.domain.Staff">
<id property="sid" column="sid"></id>
<result property="sname" column="sname"></result>
</collection>
</resultMap>
3. 多对多关系查询: 分为两类:
一类是中间表无联合主键之外的字段信息(如老师和学生), 一类是中间表还有联合主键之外的字段信息 (如: 用户和游戏角色,此时中间表会存在一个角色等级的信息 又如: 订单和产品,此时中间表会存在订单中某一产品的数量信息);
A. 中间表无其他字段 (老师和班级):
情况分析: 该种场景下, 一般不需要以中间表作为数据源查询关联信息, 故使用情景可直接分为两个一对多查询, 只不过将sql语句从二表联查增加到了三表联查;
此时实体属性不通过中间表对象进行关联, 如下:
表关系:
实体定义:
查询某个老师的所教授班级时: MyBatis的resultMap定义:
<resultMap id="mapOfFindClass" type="com.wen.domain.Teacher">
<id property="tid" column="tid"></id>
<result property="tname" column="tname"></result>
<collection property="classList" ofType="list" javaType="com.wen.domain.Class">
<id property="cid" column="cid"></id>
<result property="cname" column="cname"></result>
</collection>
</resultMap>
但是,该种方法限制了中间表字段的可扩展性, 比如: 现在学校举办活动, 将每个老师带的每个班效果做评比打分吗此时便要增加中间表字段: 分数;
B. 中间表有其它字段 (玩家和游戏角色):
情况分析: 该种情况下, 可能需要以中间表作为基础进行查询, 比如游戏角色等级排行榜的实现;
此时玩家和游戏角色的类通过中间表对象进行关联, 如下:
表关系:
定义实体:
查询某一等级的所有用户所持游戏角色信息: MyBatis的resultMap定义:
<resultMap id="mapOfAllP_R" type="com.wen.domain.playRole">
<result property="level" column="level"></result>
<association property="role" javaType="com.wen.domain.Role">
<id property="rid" column="rid"></id>
<result property="rname" column="rname"></result>
</association>
<association property="player" javaType="com.wen.domain.Player">
<id property="pid" column="pid"></id>
<result property="pname" column="pname"></result>
</association>
</resultMap>
同理: 因为两张表和中间表对应POJO都定义了相应实体集合和实体成员属性, 故: 通过resultMap联合多表查询语句可以查询任何相关信息;
RDB表关系映射问题:
1. 基于有外键的表到相应关联表的查询实际是一对一关系, 所以为了体现RDB中的外键, 需要在基本表的类中定义外键所在表对应的实体成员属性;
2.基于被外键关联表的查询, 实际是一对多关系, 即一个主键可以被多次引用为外键, 故需在基本表的类中定义集合, 泛型为外键所在的表对应实体类;
3.基于多表中间表的查询, 是实际意义上的多对多查询, 此时在中间表的类中定义相应两个关联表的实体成员属性即可;
所有支持面向对象的语言中, 包括以面向对象思维的数据传递语言(如JSON), 对象关系的维系靠的是树杈结构, 每一个实体成员属性(或者集合)都是一个节点, 如下图:
但是在RDB中, 对象关系(表关系)是以 "二维表加外键连接" 的方式实现的:
故: 通过上述分析和论述, 想实现以上两种数据模型的完整关系映射, 达到只要有关系就可以封装进一个对象的目的, 遵循以下原则即可:
1. 当对象id被作为某表外键时, 需要定义一个某表对应实体为泛型的集合作为成员属性;
2. 当引用了某表的id作为外键时, 需要定义一个某表对应类作为实体成员属性;
3. 第2条优先于第1条, 即某字段同时为主键和外键时, 仅定义实体成员属性;
前提: RDB表的设计遵循了三范式, 每张表对应一个实体类, 再根据表关系创建相应的实体或集合成员属性;
转载请标明出处: 划船一哥